<?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>Random Musings of an Insane Mind &#187; Python</title>
	<atom:link href="http://cd34.com/blog/category/programming/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://cd34.com/blog</link>
	<description>This is my blog, there are many others like it but this one is mine.</description>
	<lastBuildDate>Tue, 29 Jun 2010 04:22:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>A weekend with Tornado</title>
		<link>http://cd34.com/blog/infrastructure/a-weekend-with-tornado/</link>
		<comments>http://cd34.com/blog/infrastructure/a-weekend-with-tornado/#comments</comments>
		<pubDate>Tue, 29 Jun 2010 04:22:00 +0000</pubDate>
		<dc:creator>cd34</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Infrastructure]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=975</guid>
		<description><![CDATA[After working on a Pylons project for a week or so, there was a minor part of it that I felt didn&#8217;t need the complexity of a framework. Some quick benchmarking of the most minimal Pylons/SQLAlchemy project I could muster came in around 200 requests per second which put me at roughly 12 million requests [...]]]></description>
			<content:encoded><![CDATA[<p>After working on a <a href="http://pylonshq.com">Pylons</a> project for a week or so, there was a minor part of it that I felt didn&#8217;t need the complexity of a framework.  Some quick benchmarking of the most minimal Pylons/SQLAlchemy project I could muster came in around 200 requests per second which put me at roughly 12 million requests per day based on the typical curve.</p>
<p>Within 15 minutes of installing <a href="http://www.tornadoweb.org/">Tornado</a> and using their simple hello world example, I imported SQLAlchemy and ended up boosting this to 280 requests per second.  As I really didn&#8217;t need any of the features from the ORM, I decided to use tornado.database which isn&#8217;t much more than a bare wrapper to python-mysql.  Even with a single worker process, I was able to get 870 requests per second.  56 million requests per day, without any tuning?</p>
<p>I&#8217;m reasonably impressed.  Once I put it on production hardware, I&#8217;m thinking I&#8217;ll easily be able to count on double those numbers if not more.</p>
<p>Next weekend, <a href="http://trafficserver.apache.org/">Traffic Server</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/infrastructure/a-weekend-with-tornado/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pylons and Facebook Application Layout</title>
		<link>http://cd34.com/blog/programming/python/pylons-and-facebook-application-layout/</link>
		<comments>http://cd34.com/blog/programming/python/pylons-and-facebook-application-layout/#comments</comments>
		<pubDate>Sun, 30 May 2010 21:51:08 +0000</pubDate>
		<dc:creator>cd34</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[pylons]]></category>
		<category><![CDATA[uwsgi]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=964</guid>
		<description><![CDATA[While I spent quite a bit of time deciphering the Graph API documentation and the OAuth guides that Facebook puts forth and submitted three documentation fixes for examples that call non-existent parameters and consequently don&#8217;t work, I came to the realization that my original layout really only works if you use a single Pylons instance [...]]]></description>
			<content:encoded><![CDATA[<p>While I spent quite a bit of time deciphering the Graph API documentation and the OAuth guides that Facebook puts forth and submitted three documentation fixes for examples that call non-existent parameters and consequently don&#8217;t work, I came to the realization that my original layout really only works if you use a single Pylons instance per Facebook application.  Since we&#8217;re focused on Don&#8217;t Repeat Yourself (DRY) Principles, some thought needs to go into things.</p>
<p>First, our platform needs to be designed.  For this set of projects we&#8217;re going to use Nginx with uwsgi.  Since we&#8217;re serving static content, we&#8217;re going to set up our directories on Nginx to allow that content to be served outside our Pylons virtual environment.  Tony Landis was one of the first to provide an <a href="http://tonylandis.com/python/deployment-howt-pylons-nginx-and-uwsgi/">implementation guide</a> for uwsgi with Nginx for Pylons which provided some of the information needed to get things working.</p>
<p>Our theoretical layout looks like the following:</p>
<pre>
/webroot
  |--- /static
  |--- /fb
/virtualenv
  |--- /fbappone
  |--- /fbapptwo
</pre>
<p>Later we&#8217;ll add a CDN that does origin pulls from /webroot/static.  This application would have worked wonderfully with Varnish and ESI if the ESI could be compressed, but, setting up Nginx -> Varnish -> Nginx -> uwsgi seemed somewhat inefficient just to add compression.  The Facebook application we&#8217;ve developed is an IFrame canvas which took roughly fifteen hours to debug after the original concept was decided.  The majority of that time was spent dealing with the IFrame canvas issues.  FBML was much easier to get working properly.</p>
<p>What we end up with is a url structure like:</p>
<pre>

http://basedomain.com/

     /static/ (xd_receiver.html, jquery support modules, CSS files)
     /fb/ (Generic facebook files, support, tos, help)
     /(fbapp)/application_one/
     /(fbapp)/application_two/
</pre>
<p>As a result of this structure, we don&#8217;t need to manipulate config/routing.py as the default&#8217;s set by Pylons map things the way we want.  In the /static/ directory, we can put our CSS, js and static media files.  Remember to minify the CSS and js files and combine them if possible.</p>
<p>Our nginx config looks like:</p>
<pre>
server {
    listen   1.2.3.4:80;
    server_name  xxxxxx.com;
    access_log /var/log/nginx/xxxxxx.com-access.log;

    location ~* (css|js|png|jpe?g|gif|ico|swf|flv)$ {
        expires max;
    }

    gzip on;
    gzip_min_length 500;
    gzip_types text/plain application/xml text/html text/javascript;
    gzip_disable "MSIE [1-6]\.";

    location ^~ /static/ {
    	alias   /var/www/xxxxxx.com/static/;
    }
    location ^~ /fb/ {
    	alias   /var/www/xxxxxx.com/fb/;
    }
    location / {
        uwsgi_pass  unix:/tmp/uwsgi.sock;
        include     uwsgi_params;
    }
}
</pre>
<p>We could modify the nginx config to pull / from the static page, but, we&#8217;re actually capturing that with a root controller that knows what applications reside below it as a directory of sorts.</p>
<p>We used Debian which doesn&#8217;t support uwsgi yet.  A brief set of instructions follows which should work on any Debian based distribution as well:</p>
<pre>
apt-get install libxml2-dev dpkg-dev debhelper
cd /usr/src
apt-get source nginx
wget http://projects.unbit.it/downloads/uwsgi-0.9.4.4.tar.gz
tar xzf uwsgi-0.9.4.4.tar.gz
cd nginx
vi debian/rules
  add:  --add-module=/usr/src/uwsgi-0.9.4.4/nginx/ \
dpkg-buildpackage
dpkg -i ../nginx_0.7.65-5_i386.deb
mkdir /usr/local/nginx/
cp /usr/src/uwsgi-0.9.4.4/nginx/uwsgi_params /etc/nginx
</pre>
<p>/etc/nginx/uwsgi_params, add:</p>
<pre>
uwsgi_param  SCRIPT_NAME        /;
</pre>
<p>Note: I had problems with 0.9.5.1 and paster enabled wsgi applications which caused issues with Pylons.</p>
<p>Our uwsgi command line for development:</p>
<pre>
/usr/src/uwsgi-0.9.4.4/uwsgi -s /tmp/uwsgi.sock -C -iH /var/www/facebook/ --paste config:/var/www/facebook/fpapp/development.ini
</pre>
<p>One of the things that made Facebook integration difficult was somewhat incomplete documentation or even incorrect documentation on Facebook&#8217;s site.  While the Graph API is new, it is quite a bit more powerful.  While they do have official support, I think I&#8217;ll use <a href="http://github.com/bbangert/velruse">velruse</a> for OAuth integration next time and use the Python-SDK for the Graph API integration.  See my previous post on using <a href="/blog/framework/using-pylons-for-a-facebook-application/">Pylons for a Facebook Application</a> for a little more detailed information on how to get the application working.</p>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/programming/python/pylons-and-facebook-application-layout/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Why do you use an Object Relational Mapping (ORM) System in Development?</title>
		<link>http://cd34.com/blog/programming/python/why-do-you-use-an-object-relational-mapping-orm-system-in-development/</link>
		<comments>http://cd34.com/blog/programming/python/why-do-you-use-an-object-relational-mapping-orm-system-in-development/#comments</comments>
		<pubDate>Mon, 12 Oct 2009 17:51:54 +0000</pubDate>
		<dc:creator>cd34</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[orm]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[pylons]]></category>
		<category><![CDATA[smarty]]></category>
		<category><![CDATA[sqlalchemy]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=819</guid>
		<description><![CDATA[Here&#8217;s a programmer that is saying goodbye to ORMs at Hatful of Hollow. And another site offering a tutorial of sorts dealing with ORMs Why should you use an ORM. While both have their points, both have missed a fundamental benefit that an ORM hands you. Most of my development is in Pylons. Django&#8217;s ORM [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a programmer that is saying goodbye to ORMs at <a href="http://www.hatfulofhollow.com/posts/code/farewell-to-orms.html">Hatful of Hollow</a>.  </p>
<p>And another site offering a tutorial of sorts dealing with ORMs <a href="http://karwin.blogspot.com/2009/01/why-should-you-use-orm.html">Why should you use an ORM</a>.</p>
<p>While both have their points, both have missed a fundamental benefit that an ORM hands you.</p>
<p>Most of my development is in Pylons.  Django&#8217;s ORM and template language can do the same thing.  A programmer that has used PHP/Smarty to develop large scale systems will likely resist ORMs.  After working with a team to develop 90k+ lines of PHP/Smarty over a six year period, making the shift required a paradigm shift.</p>
<p>Let&#8217;s consider the following structure.  We have a cp_ticket table and a cp_ticket_detail table.  A Ticket can have multiple detail records.  The output we wish to have is:</p>
<pre>
ticket id, ticket header information
         ticket detail line
         ticket detail line #2
ticket id, ticket header information
         ticket detail line
         ticket detail line #2
         ticket detail line #3
ticket id, ticket header information
         ticket detail line
         ticket detail line #2
</pre>
<p>Our model:</p>
<pre>
class cp_ticket(DeclarativeBase):
    __tablename__ = 'cp_ticket'

    ticket_id = Column(mysql.MSBigInteger(20, unsigned = True), primary_key=True, autoincrement = True)
    priority = Column(mysql.MSEnum('1','2','3','4','5'), default = '3')

    ticket_detail = relation('cp_ticket_detail', order_by='cp_ticket_detail.ticket_detail_id')

class cp_ticket_detail(DeclarativeBase):
    __tablename__ = 'cp_ticket_detail'

    ticket_id = Column(mysql.MSBigInteger(20, unsigned = True), ForeignKey('cp_ticket.ticket_id'), default = '0')
    ticket_detail_id = Column(mysql.MSBigInteger(20, unsigned = True), primary_key=True, autoincrement = True)
    stamp = Column(mysql.MSTimeStamp, PassiveDefault('CURRENT_TIMESTAMP'))
    detail = Column(mysql.MSLongText, default = '')
</pre>
<p>Our query to pass to our template:</p>
<pre>
        tickets = meta.Session.query(cp_ticket).filter(cp_ticket.client_id==1).all()
</pre>
<p>Compared with the query as you would write it without an ORM:</p>
<pre>
select * from cp_ticket,cp_ticket_detail where client_id=1 and cp_ticket.ticket_id=cp_ticket_detail.ticket_id;
</pre>
<p>Both are doing the same fundamental thing, but, the ORM maps the results almost identical to the way we want to display the data.  This makes template design easy.</p>
<p>Using Mako, we use the following code to display the results:</p>
<pre>
&lt;table border="1">
 &lt;tr>&lt;th>Ticket ID&lt;/th>&lt;th>Status&lt;/th>&lt;th>Detail&lt;/th>&lt;/tr>
%for ticket in tmpl_context.tickets:
  &lt;tr>
    &lt;td>&lt;strong>${ticket.ticket_id}&lt;/strong>&lt;/td>
    &lt;td>&lt;strong>${ticket.priority}&lt;/strong>&lt;/td>
  &lt;/tr>
  %for detail in ticket.ticket_detail:
  &lt;tr>
    &lt;td>&lt;/td>
    &lt;td>${detail.stamp}&lt;/td>
    &lt;td>${detail.detail}&lt;/td>
  &lt;/tr>
  % endfor
% endfor
&lt;/table>
</pre>
<p>To do the same thing without using an ORM, you need to revert to a control break structure similar to the following:</p>
<pre>
current_ticket=0
for ticket in tickets:
  if (current_ticket != ticket.ticket_id):
    #new row, print the header
    print "&lt;tr>&lt;td>first piece&lt;/td>&lt;/tr>"
    current_ticket = ticket.ticket_id
  # print our detail row
  print "&lt;tr>&lt;td>&lt;/td>&lt;td>stamp and detail&lt;/td>&lt;/tr>"
</pre>
<p>Control Break structures require you to be able to set a variable within your template language.  Some template languages don&#8217;t allow that.  If your template language (in any language) can&#8217;t do variable assignments in the template, guess where your html generation logic needs to go?  </p>
<p>With an ORM, the template contains your display logic.  Your webmaster/design team can modify the template without having to modify html contained within your code.  The loops are simple to understand and designers usually have little problem avoiding the lines that start with %.</p>
<p>Sure, you could wrap much of this logic in your template to do the control-break structure, but, as you get more complex data, deciding how to display the data requires a define or some other functionality.</p>
<p>An ORM adds some insulation to the process, but, the result is a much easier page structure when displaying related data.  Granted there are some performance hits and SQLAlchemy appears to create some queries that are not optimal, unless there is a tremendous performance hit, I think the benefits of the ORM for developing a web application are tremendous.</p>
<p>Once you move into an environment where you are dealing with multiple developers, having a defined schema with comments is much easier than using reflection to figure out what the meaning of a status field as enum(&#8216;U&#8217;,'A&#8217;,'P&#8217;,'C&#8217;,'R&#8217;,'S&#8217;).  </p>
<p>However, as the original poster mentions, you can do raw SQL within SQLAlchemy and do all of your work with reflection as he has done with his ORM^H^H^H, abstraction.  If he&#8217;s still using SQLAlchemy, he can selectively decide when to use it and when to avoid it.</p>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/programming/python/why-do-you-use-an-object-relational-mapping-orm-system-in-development/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mysql-python and Snow Leopard</title>
		<link>http://cd34.com/blog/programming/python/mysql-python-and-snow-leopard/</link>
		<comments>http://cd34.com/blog/programming/python/mysql-python-and-snow-leopard/#comments</comments>
		<pubDate>Tue, 01 Sep 2009 16:21:32 +0000</pubDate>
		<dc:creator>cd34</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[mac os/x]]></category>
		<category><![CDATA[mysql-python]]></category>
		<category><![CDATA[snow leopard]]></category>
		<category><![CDATA[virtualenv]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=750</guid>
		<description><![CDATA[After the upgrade to Snow Leopard, mysql-python cannot be installed through easy_install. * Install mysql&#8217;s x86_64 version from the .dmg file (I had a problem doing this when booted into the 64bit kernel, a 32bit kernel macbook had no problem) With the 64bit kernel, the system reported &#8216;no mountable file systems&#8217; when trying to mount [...]]]></description>
			<content:encoded><![CDATA[<p>After the upgrade to Snow Leopard, mysql-python cannot be installed through easy_install.</p>
<p>* Install <a href="http://dev.mysql.com/downloads/mysql/5.1.html#downloads">mysql&#8217;s x86_64</a> version from the .dmg file (I had a problem doing this when booted into the 64bit kernel, a 32bit kernel macbook had no problem)  With the 64bit kernel, the system reported &#8216;no mountable file systems&#8217; when trying to mount the .dmg file.  A reboot into 32bit mode allowed the .dmg to be mounted and installed.<br />
* Change into your virtual environment if desired, source bin/activate<br />
* fetch <a href="http://sourceforge.net/projects/mysql-python/">MySQL-python-1.2.3c1</a></p>
<pre>
tar xzf MySQL-python-1.2.3c1.tar.gz
cd MySQL-python-1.2.3c1
ARCHFLAGS='-arch x86_64' python setup.py build
ARCHFLAGS='-arch x86_64' python setup.py install
</pre>
<p>If everything works, you should see:</p>
<pre>
$ python
Python 2.6.1 (r261:67515, Jul  7 2009, 23:51:51)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import MySQLdb
>>>
</pre>
<p>Some of the possible things you&#8217;ll encounter:</p>
<p>After <strong>python setup.py build</strong>:</p>
<pre>
ld: warning: in build/temp.macosx-10.6-universal-2.6/_mysql.o, file is not of required architecture
ld: warning: in /usr/local/mysql/lib/libmysqlclient.dylib, file is not of required architecture
ld: warning: in /usr/local/mysql/lib/libmygcc.a, file is not of required architecture
ld: warning: in build/temp.macosx-10.6-universal-2.6/_mysql.o, file is not of required architecture
ld: warning: in /usr/local/mysql/lib/libmysqlclient.dylib, file is not of required architecture
ld: warning: in /usr/local/mysql/lib/libmygcc.a, file is not of required architecture
</pre>
<p>This means that you have the i386 version of mysql installed.  Or, if you have the x86_64 version, you have didn&#8217;t include the proper ARCHFLAGS command.</p>
<p><strong>ImportError: dynamic module does not define init function (init_mysql)</strong></p>
<p>this means that the easy_install or the build/install process was run which tried to build a ppc/i386/x86_64 combined build.</p>
<p>If you see messages like:</p>
<pre>
$ python
Python 2.6.1 (r261:67515, Jul  7 2009, 23:51:51)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import MySQLdb
/Users/xxxxx/Python/django/lib/python2.6/site-packages/MySQL_python-1.2.3c1-py2.6-macosx-10.6-universal.egg/_mysql.py:3: UserWarning: Module _mysql was already imported from /Users/xxxxx/Python/django/lib/python2.6/site-packages/MySQL_python-1.2.3c1-py2.6-macosx-10.6-universal.egg/_mysql.pyc, but /Users/xxxxx/Python/django/MySQL-python-1.2.3c1 is being added to sys.path
>>>
</pre>
<p>Then you are still in the build directory.  cd ..  and try again.</p>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/programming/python/mysql-python-and-snow-leopard/feed/</wfw:commentRss>
		<slash:comments>37</slash:comments>
		</item>
		<item>
		<title>Google&#8217;s App Engine goof</title>
		<link>http://cd34.com/blog/infrastructure/googles-app-engine-goof/</link>
		<comments>http://cd34.com/blog/infrastructure/googles-app-engine-goof/#comments</comments>
		<pubDate>Sat, 04 Jul 2009 03:54:58 +0000</pubDate>
		<dc:creator>cd34</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Infrastructure]]></category>
		<category><![CDATA[Google App Engine]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=697</guid>
		<description><![CDATA[While Google&#8217;s App Engine is a well planned service and it does work incredibly well for what it does, sometimes things break due to resource limits, etc. While the app engine platform is still running, it appears to be an issue with this particular application&#8217;s committed resources. The App Gallery has exceeded it&#8217;s memory quota.]]></description>
			<content:encoded><![CDATA[<p>While Google&#8217;s App Engine is a well planned service and it does work incredibly well for what it does, sometimes things break due to resource limits, etc.</p>
<p>While the app engine platform is still running, it appears to be an issue with this particular application&#8217;s committed resources.  The <a href="http://appgallery.appspot.com/">App Gallery</a> has exceeded it&#8217;s memory quota.</p>
<p><a href="http://cd34.colocdn.com/blog/wp-content/uploads/2009/07/appsgallery1.png"><img src="http://cd34.colocdn.com/blog/wp-content/uploads/2009/07/appsgallery1-300x197.png" alt="Google App Engine App Gallery" title="Google App Engine App Gallery" width="300" height="197" class="aligncenter size-medium wp-image-700" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/infrastructure/googles-app-engine-goof/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>User Interface Design</title>
		<link>http://cd34.com/blog/infrastructure/user-interface-design/</link>
		<comments>http://cd34.com/blog/infrastructure/user-interface-design/#comments</comments>
		<pubDate>Wed, 24 Jun 2009 05:46:26 +0000</pubDate>
		<dc:creator>cd34</dc:creator>
				<category><![CDATA[Framework]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Infrastructure]]></category>
		<category><![CDATA[formencode]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[smarty]]></category>
		<category><![CDATA[sqlalchemy]]></category>
		<category><![CDATA[toscawidgets]]></category>
		<category><![CDATA[turbogears]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=676</guid>
		<description><![CDATA[Programmers are not designers. Technical people should not design User Interfaces. * 810 source files * 90658 lines of code * 10213 lines of html For an internal project tasked to a series of programmers throughout the years without enough oversight, it is a mass of undocumented code with multiple programming styles. PHP allowed lazy [...]]]></description>
			<content:encoded><![CDATA[<p>Programmers are not designers.  Technical people should not design User Interfaces.</p>
<p>* 810 source files<br />
* 90658 lines of code<br />
* 10213 lines of html </p>
<p>For an internal project tasked to a series of programmers throughout the years without enough oversight, it is a mass of undocumented code with multiple programming styles.  PHP allowed lazy programming, Smarty didn&#8217;t have some of the finesse required, so, the User Interface suffered.  Functional but confusing to anyone that hadn&#8217;t worked intimately with the interface or been walked through it.</p>
<p>The truest statement is that it is easier for me to do things through the MySQL command line than through the application.  While this does have a tendency to introduce possible typos, it has altered SQL practices here.</p>
<p><code>update table set value=123 where othervalue=246;</code></p>
<p>could have an accidental typo of </p>
<p><code>update table set value=123 where othervalue-=246;</code></p>
<p>which would have completely unintended consequences.  One typo altered the DNS entries for 48000 records.  Shortly after that typo, ingrained in company policy was that I never wanted to ever see a query like that executed in the command line regardless of how simple the command.</p>
<p>Even within code, the above command would be entered as:</p>
<p><code>update table set value=123 where othervalue in (246);</code></p>
<p>This prevented a number of potential typos.  Even limit clauses with deletions were enforced to make sure things didn&#8217;t go too haywire in an update.</p>
<p>With Python, indenting is mandatory which results in multiple programmer&#8217;s code looking similar and easier to troubleshoot.  Utilizing SQLAlchemy which enforces bind variables when talking with the database engine, we&#8217;ve eliminated the potential for a typo updating too many records.  Even cascade deletes are enforced in SQLAlchemy even when running on top of MyISAM.  With MVC, our data model is much better defined and we&#8217;re not tied down to remembering the relationship between two tables and possible dependencies.  Conversion from the existing MySQL database to a DeclarativeBase model hasn&#8217;t been without issues, but, a simple python program allowed the generation of a simple model that took care of most of the issues.  Hand tweaking the database model while developing the application has allowed for quite a bit of insight into issues that had been worked around rather than making adjustments to the database.</p>
<p>Fundamental design issues in the database structure were worked around with code rather than fixed.  Data that should have been retained was not, relationships between tables was defined in code rather than in the database leading to a painful conversion.</p>
<p>When it was decided to rewrite the application in Python using TurboGears, I wasn&#8217;t that familiar with the codebase nor the user interface.  Initially it was envisioned that the templates would be copied and the backend engine would be written to power those templates.  After a few hours running through the application, and attempting the conversion on a number of templates, I realized the application was functional but it was extremely difficult to use in its current state.  So much for having a programmer design an interface.</p>
<p>Some functionality from the existing system was needed so I peered into the codebase and was unprepared for that surprise.  At this point it became evident that a non-programmer had designed the interface.  While Smarty was a decent template language, it was not a formtool, so, methods were designed to give a consistent user experience when dealing with error handling.  A single php file was responsible for display, form submission and validation and writing to the database for each &#8216;page&#8217; in the application.  The code inside should have been straightforward.</p>
<p>* Set up default CSS classes for each form field for an &#8216;ok&#8217; result<br />
* Validate any passed values and set the CSS class as &#8216;error&#8217; for any value that fails validation<br />
* Insert/Update the record if the validation passes<br />
* Display the page</p>
<p>Some validation takes place numerous times throughout the application, and, for some reason one of the &#8216;coders&#8217; decided that copy and paste of another function that used that same validation code was better than writing a function to do the validation.  Of course when that validation method needed to be changed, it needed to be changed in eight places.</p>
<p>So, what should have been somewhat simple has changed considerably:</p>
<p>* Evaluate each page<br />
* Redesign each page to make the process understandable<br />
* Adjust terminology to make it understandable to the application&#8217;s users<br />
* modify the database model<br />
* rewrite the form and validation</p>
<p>A process that should have been simple has turned into quite a bit more work than anticipated.  Basically, development boils down to looking at the page, figuring out what it should be, pushing the buttons to see what they do and rewriting from scratch.</p>
<p>TurboGears has added a considerable amount of efficiency to the process.  One page that dealt with editing a page of information was reduced from 117 lines of code to 12 lines of code.  Since TurboGears uses ToscaWidgets and Formencode, validation and form presentation is removed from the code resulting in a controller that contains the code that modifies the tables in the database with validated input.  Since Formencode already has 95% of the validators that are needed for this project, we can rest assured that someone else has done the work to make sure that field will be properly validated.  Other validation methods can be maintained and self-tested locally, but, defined in such a manner that they are reused throughout the application rather than being cut and pasted into each model that is validating data.  In addition, bugs should be much less frequent as a result of a much-reduced codebase.</p>
<p>Due to the MVC framework and the libraries selected by the developers at TurboGears, I wouldn&#8217;t be surprised if the new codebase is 10%-15% the size of the existing application with greater functionality.  The code should be more maintainable as python enforces some structure which will increase readability.</p>
<p>While I am not a designer, even using ToscaWidgets and makeform, the interface is much more consistent.  Picking the right words, adding the appropriate help text to the fields and making sure things work as expected has resulted in a much cleaner, understandable interface.</p>
<p>While there are some aspects of ToscaWidgets that are a little too structured for some pages, our current strategy is to develop the pages using ToscaWidgets or makeform to make things as clear as possible making notes to overload the Widget class for our special forms at a later date.</p>
<p>While it hasn&#8217;t been a seamless transition, it did provide a good opportunity to rework the site and see a number of the problems that the application has had for a long time.</p>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/infrastructure/user-interface-design/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Recursive Category Table in Python</title>
		<link>http://cd34.com/blog/programming/python/recursive-category-table-in-python/</link>
		<comments>http://cd34.com/blog/programming/python/recursive-category-table-in-python/#comments</comments>
		<pubDate>Sat, 13 Jun 2009 16:16:59 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[category table]]></category>
		<category><![CDATA[recursive]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=650</guid>
		<description><![CDATA[While working with a project, the problem with the recursive category table being built came up. The table holds the parent_id of the category name and the result is a list with the id and the name of the category. The category name is prepended with spaces equivalent to the level of indentation. model: class [...]]]></description>
			<content:encoded><![CDATA[<p>While working with a project, the problem with the recursive category table being built came up.  The table holds the parent_id of the category name and the result is a list with the id and the name of the category.  The category name is prepended with spaces equivalent to the level of indentation.</p>
<p>model:</p>
<pre>
class AchievementCategory(DeclarativeBase):
	__tablename__ = 'achievement_categories'

	id = Column(mysql.MSBigInteger(20, unsigned = True), primary_key = True)
	parent_id = Column(mysql.MSBigInteger(20, unsigned = True), default = 0)
	name = Column(Unicode(80))
</pre>
<p>code:</p>
<pre>
def get_cats(n = 0, c_list = [], level = 0):
	sql = DBSession.query(AchievementCategory).filter_by(parent_id = n).order_by(AchievementCategory.name).all()
	for e in sql:
		c_list.append([e.id, level * " " + e.name, level])
		get_cats(e.id, c_list, level+1)
	return c_list

print get_cats()
</pre>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/programming/python/recursive-category-table-in-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TurboGears, Tableform and a callable option to a Widget</title>
		<link>http://cd34.com/blog/programming/python/turbogears-tableform-and-a-callable-option-to-a-widget/</link>
		<comments>http://cd34.com/blog/programming/python/turbogears-tableform-and-a-callable-option-to-a-widget/#comments</comments>
		<pubDate>Wed, 10 Jun 2009 16:31:20 +0000</pubDate>
		<dc:creator>cd34</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[tableform]]></category>
		<category><![CDATA[toscawidgets]]></category>
		<category><![CDATA[turbogears]]></category>
		<category><![CDATA[widget]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=641</guid>
		<description><![CDATA[While doing some TurboGears development I ran into an issue where I needed to generate a select field&#8217;s options from the database that was dependent on authentication. Since defining the query in the model results in a cached result when the class is instantiated, the query couldn&#8217;t be defined there. There are multiple mentions of [...]]]></description>
			<content:encoded><![CDATA[<p>While doing some TurboGears development I ran into an issue where I needed to generate a select field&#8217;s options from the database that was dependent on authentication.  Since defining the query in the model results in a cached result when the class is instantiated, the query couldn&#8217;t be defined there.  There are multiple mentions of using a callable to deal with this situation, but, no code example.</p>
<p>From this posting in <a href="http://groups.google.com/group/turbogears/browse_thread/thread/b390280051e0f7d8/">Google Groups for TurboGears</a>, we were able to figure out the code that made this work.</p>
<p>template:</p>
<pre>&lt;div xmlns="http://www.w3.org/1999/xhtml"
      xmlns:py="http://genshi.edgewall.org/"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      py:strip="">

${tmpl_context.form(value=value)}

&lt;/div></pre>
<p>controller:</p>
<pre>    @expose('cp.templates.template')
    def form(self):
        c.form = TestForm()
        c.availips = [[3,3],[2,2]]
        return dict(template='form',title='Test Form',value=None)</pre>
<p>model:</p>
<pre>from pylons import c

def get_ips():
    return c.availips

class TestForm(TableForm):
    action = '/test/testadd'
    submit_text = 'Add test'

    class fields(WidgetsList):
        User = TextField(label_text='FTP Username', size=40, validator=NotEmpty())
        Password = PasswordField(label_text='FTP Password', size=40, validator=NotEmpty())
        ip = SingleSelectField(label_text="Assign to IP",options=get_ips)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/programming/python/turbogears-tableform-and-a-callable-option-to-a-widget/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Combined Web Site Logging splitter for AWStats</title>
		<link>http://cd34.com/blog/programming/python/combined-web-site-logging-splitter-for-awstats/</link>
		<comments>http://cd34.com/blog/programming/python/combined-web-site-logging-splitter-for-awstats/#comments</comments>
		<pubDate>Fri, 29 May 2009 20:59:43 +0000</pubDate>
		<dc:creator>cd34</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[awstats]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=639</guid>
		<description><![CDATA[AWStats has an interesting problem when working with combined logging. When you have 500 domains and combined logfiles at roughly 2 gigabytes a day, awstats spends a lot of time shuffling through all of the log files to return the results. The simple solution appeared to be a small python script that read the awstats [...]]]></description>
			<content:encoded><![CDATA[<p>AWStats has an interesting problem when working with combined logging.  When you have 500 domains and combined logfiles at roughly 2 gigabytes a day, awstats spends a lot of time shuffling through all of the log files to return the results.  The simple solution appeared to be a small python script that read the awstats config directory and split the logfile into pieces so that awstats could run on individual logfiles.  It requires one loop through the combined logfile to create all of the logfiles, rather than looping through the 2 gigabyte logfile for each domain when awstats was set up with combined logging.</p>
<pre>
#!/usr/bin/python

import os,re
from string import split

dirs = os.listdir('/etc/awstats')

domainlist = {}

for dir in dirs:
  if (re.search('\.conf$',dir)):
    dom = re.sub('^awstats\.', '', dir)
    dom = re.sub('\.conf$', '', dom)
    domainlist[dom] = 1

loglist = open('/var/log/apache2/combined-access.log.1','r')
for line in loglist:
  (domain,logline) = line.split(None, 1)
  if (domain in domainlist):
    if (domainlist[domain] == 1):
      domainlist[domain] = open('/var/log/apache2/' + domain + '-access.log.1', 'w')
    domainlist[domain].write(logline)
</pre>
<p>While the code isn&#8217;t particularly earthshattering, it cut down log processing to roughly 20 minutes per day rather than the previous 16-30 hours per day.</p>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/programming/python/combined-web-site-logging-splitter-for-awstats/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Python, Perl and PHP interoperability with pack and unpack</title>
		<link>http://cd34.com/blog/programming/python/python-perl-and-php-interoperability-with-pack-and-unpack/</link>
		<comments>http://cd34.com/blog/programming/python/python-perl-and-php-interoperability-with-pack-and-unpack/#comments</comments>
		<pubDate>Mon, 27 Apr 2009 04:40:40 +0000</pubDate>
		<dc:creator>cd34</dc:creator>
				<category><![CDATA[Programming Languages]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pack]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[unpack]]></category>

		<guid isPermaLink="false">http://cd34.com/blog/?p=629</guid>
		<description><![CDATA[Perl has very powerful capabilities for dealing with structures.  PHP&#8217;s support of those structures was based on Perl&#8217;s wisdom.  Python went a different direction. Perl pack/unpack definitions PING_FORMAT =&#62; &#8216;(a4n2N2N/a*)@245&#8242;; TASK_FORMAT =&#62; &#8216;a4NIN/a*a*&#8217;; RETR_FORMAT =&#62; &#8216;a4N/a*N&#8217;; ENCPAYLOAD_FORMAT =&#62; &#8216;Na*&#8217;; PHP pack/unpack definitions define(&#8216;TASK_FORMAT&#8217;, &#8216;a4NINa*a*&#8217;); define(&#8220;ENCPAYLOAD_FORMAT&#8221;,&#8217;Na*&#8217;); For a communications package written in perl that communicates with [...]]]></description>
			<content:encoded><![CDATA[<p>Perl has very powerful capabilities for dealing with structures.  PHP&#8217;s support of those structures was based on Perl&#8217;s wisdom.  Python went a different direction.</p>
<p>Perl pack/unpack definitions</p>
<blockquote><p>PING_FORMAT =&gt; &#8216;(a4n2N2N/a*)@245&#8242;;<br />
TASK_FORMAT =&gt; &#8216;a4NIN/a*a*&#8217;;<br />
RETR_FORMAT =&gt; &#8216;a4N/a*N&#8217;;<br />
ENCPAYLOAD_FORMAT =&gt; &#8216;Na*&#8217;;</p></blockquote>
<p>PHP pack/unpack definitions</p>
<blockquote><p>define(&#8216;TASK_FORMAT&#8217;, &#8216;a4NINa*a*&#8217;);<br />
define(&#8220;ENCPAYLOAD_FORMAT&#8221;,&#8217;Na*&#8217;);</p></blockquote>
<p>For a communications package written in perl that communicates with 32 bit and 64 bit machines that may not share the same endian structure.  The problem I&#8217;ve run into now is that Python does not support the Perl method, and, I don&#8217;t know why they didn&#8217;t at least offer some compatibility.  pack and unpack give enormous power to communication systems between machines and their support of the perl methods allowed for reasonable interoperability between the two platforms.</p>
<p>Python on the other hand opted to not support some of the features, which was one issue, but, their requirement is that you cannot send variable length packets.</p>
<p>In Python, we&#8217;re able to replicate N, network endian Long by using !L:</p>
<p>&gt;&gt;&gt; import struct<br />
&gt;&gt;&gt; print struct.unpack(&#8216;!L&#8217;,'\0\0\1\0&#8242;);<br />
(256,)</p>
<p>However, there is no method to support a variable length payload behind that value.  We&#8217;re able to set a fixed length like 5s, but, this means that we&#8217;ve got to know the length of the payload being sent.</p>
<p>&gt;&gt;&gt; print struct.unpack(&#8216;!L5s&#8217;,'\0\0\1\0abcde&#8217;);<br />
(256, &#8216;abcde&#8217;)</p>
<p>If we overstate the size of the field, Python is more than happy to tell us that the payload length doesn&#8217;t match the length of the data.</p>
<p>&gt;&gt;&gt; print struct.unpack(&#8216;!L8s&#8217;,'\0\0\1\0abcde&#8217;);<br />
Traceback (most recent call last):<br />
File &#8220;&lt;stdin&gt;&#8221;, line 1, in &lt;module&gt;<br />
File &#8220;/usr/lib/python2.5/struct.py&#8221;, line 87, in unpack<br />
return o.unpack(s)<br />
struct.error: unpack requires a string argument of length 12</p>
<p>The cheeseshop/pypi seems to show no suitable alternative which brings up a quandry.  For this particular solution, I&#8217;ll write a wrapper function to do the heavy lifting on the two unpack strings I need to deal with and then I&#8217;ll debate pulling the perl unpack/pack routines out of the perl source and wrapping it into an .egg, possibly for distribution.</p>
]]></content:encoded>
			<wfw:commentRss>http://cd34.com/blog/programming/python/python-perl-and-php-interoperability-with-pack-and-unpack/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
