<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2752288660688812438</id><updated>2012-01-31T12:15:42.767+02:00</updated><category term='mobile'/><category term='linux'/><category term='firefox'/><category term='tricks'/><category term='podcast'/><category term='tools'/><category term='emacs keyboard'/><category term='python'/><category term='web'/><category term='bugs'/><category term='web frameworks'/><category term='django newforms'/><category term='latex'/><category term='cakephp'/><category term='email'/><category term='postscript scribus sibelius'/><category term='pdf python'/><category term='thunderbird'/><category term='django'/><category term='bash python javascript bookmarklet youtube'/><title type='text'>A. Kaihola's Pocket Fluff</title><subtitle type='html'>Mostly about web development and F/LOSS software</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>12</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-7207850134332623060</id><published>2008-09-18T22:20:00.005+03:00</published><updated>2008-09-19T08:48:37.529+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash python javascript bookmarklet youtube'/><title type='text'>YouTube archiver</title><content type='html'>Here's a fun experiment with combining various tools to easily make my &lt;a href="http://mythtv.org/"&gt;MythTV&lt;/a&gt; box download &lt;a href="http://www.youtube.com/"&gt;YouTube&lt;/a&gt; videos with one click in the browser on another computer.&lt;br /&gt;&lt;br /&gt;Proceeding from client to server, the first component is a &lt;a href="http://www.mozilla-europe.org/en/firefox/"&gt;Firefox&lt;/a&gt; bookmarklet for sending the URL of the current page to the MythTV box:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;javascript:(function(){window.open('http://mythtvbox.mydomain.com:12345/'+window.location);})()&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;On the MythTV box I have a &lt;a href="http://python.org/"&gt;Python&lt;/a&gt; script listening for incoming HTTP requests on port 12345. The script uses &lt;a href="http://twistedmatrix.com/trac/wiki/TwistedWeb"&gt;twisted.web&lt;/a&gt;, so &lt;code&gt;apt-get python-twisted-web&lt;/code&gt; (on Debian-based distros) is needed.&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;#!/usr/bin/python&lt;br /&gt;&lt;br /&gt;from twisted.web import server, resource&lt;br /&gt;from twisted.internet import reactor&lt;br /&gt;&lt;br /&gt;class DownloadRequestHandler(resource.Resource):&lt;br /&gt;  isLeaf = True&lt;br /&gt;  def render_GET(self, request):&lt;br /&gt;      try:&lt;br /&gt;          video_id = request.args['v'][0]&lt;br /&gt;      except:&lt;br /&gt;          msg = "Can't parse URL %r" % request.path&lt;br /&gt;          return msg&lt;br /&gt;      file('/home/akaihola/.download-queue/youtube.com/%s' % video_id, 'w').close()&lt;br /&gt;      return 'Downloading YouTube %s' % video_id&lt;br /&gt;&lt;br /&gt;site = server.Site(DownloadRequestHandler())&lt;br /&gt;reactor.listenTCP(12345, site)&lt;br /&gt;reactor.run()&lt;/pre&gt;&lt;br /&gt;At this point, whenever I select the "Download YouTube" bookmarklet while viewing a YouTube video, a file named after the ID of the video magically appears on the MythTV box.&lt;br /&gt;&lt;br /&gt;For actual downloading of the content I use the &lt;a href="http://www.arrakis.es/%7Erggi3/youtube-dl/youtube-dl"&gt;youtube-dl&lt;/a&gt; script. The following bash script downloads content as long as files corresponding to video IDs exist, deletes those files after downloading, and waits for new files to appear. Waiting for new files is implemented efficiently using the &lt;a href="http://inotify-tools.sourceforge.net/#info"&gt;inotifywait&lt;/a&gt; tool (&lt;code&gt;apt-get inotify-tools&lt;/code&gt; on Debian-based distros). Only a small slice of my bandwidth is allowed for downloads during daytime, and in the night it can freely saturate the connection.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;QUEUEDIR=/home/akaihola/.download-queue/youtube.com&lt;br /&gt;TEMPLATE="/var/lib/mythtv/videos/%(stitle)s-%(id)s.%(ext)s"&lt;br /&gt;cd $QUEUEDIR&lt;br /&gt;while true; do&lt;br /&gt;while [ "`ls`" ]; do&lt;br /&gt;  VIDEO=`ls -1|head -1`&lt;br /&gt;  RATE=$( [ `date +%H` -gt 6 ] &amp;amp;&amp;amp; echo "-r 30k" )&lt;br /&gt;  ~/bin/youtube-dl -b $RATE -o $TEMPLATE http://youtube.com/watch?v=$VIDEO&lt;br /&gt;  rm $VIDEO&lt;br /&gt;done&lt;br /&gt;inotifywait -e create $QUEUEDIR&lt;br /&gt;done&lt;/pre&gt;&lt;br /&gt;Both scripts are running as services in the background on the MythTV server ready to queue and download videos. The videos appear in the "Watch Videos" menu of the MythTV box ready for enjoyment.&lt;br /&gt;&lt;br /&gt;Voilà!* It works! Improvements appreciated.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:78%;"&gt;*or "Viola!" as I've seen many native English speakers happily saluting their favorite string instrument after achieving a desired goal&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Update:&lt;/span&gt; Insufficient bash experience. Wouldn't work for &gt;1 files in the queue. Fixed now, but &lt;code&gt;[ "`ls`" ]&lt;/code&gt; feels like a bit awkward way to test if a directory is not empty.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-7207850134332623060?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/7207850134332623060/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=7207850134332623060' title='4 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/7207850134332623060'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/7207850134332623060'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2008/09/youtube-archiver.html' title='YouTube archiver'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-353763618035574011</id><published>2008-09-15T09:25:00.002+03:00</published><updated>2008-09-15T09:35:19.276+03:00</updated><title type='text'>Reason for Dovecot's memory hunger</title><content type='html'>One runs into strange things when using rare combinations of software. I've been wondering why the &lt;a href="http://dovecot.org/"&gt;Dovecot&lt;/a&gt; IMAP process on our mailserver grows to 50 or 60 MB when I'm syncing my mailbox with &lt;a href="http://software.complete.org/software/projects/show/offlineimap"&gt;OfflineIMAP&lt;/a&gt;. It turns out the reason is in how OfflineIMAP identifies messages and how Dovecot's caching reacts to that.&lt;br /&gt;&lt;br /&gt;I won't pretend I understand the technical details, but here's the &lt;a href="http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=435959"&gt;bug report&lt;/a&gt; and &lt;a href="http://www.dovecot.org/list/dovecot/2007-August/024841.html"&gt;some discussion&lt;/a&gt;. The quick solution is to &lt;a href="http://dovecot.org/list/dovecot/2007-August/024852.html"&gt;delete&lt;/a&gt; overgrown dovecot index/cache files for any large folders to be synchronized. Dovecot 1.1's &lt;a href="http://tools.ietf.org/html/rfc4315"&gt;UIDPLUS&lt;/a&gt; support may also help, but that version isn't available for Etch even in the &lt;a href="http://backports.org/"&gt;backports&lt;/a&gt; repository, so I'm not trying it any time soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-353763618035574011?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/353763618035574011/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=353763618035574011' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/353763618035574011'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/353763618035574011'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2008/09/reason-for-dovecots-memory-hunger.html' title='Reason for Dovecot&apos;s memory hunger'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-8234768673509948782</id><published>2007-10-03T20:23:00.000+03:00</published><updated>2007-10-03T20:25:40.319+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='postscript scribus sibelius'/><title type='text'>Why Scribus fails to import Sibelius music</title><content type='html'>I finally found an explanation for why I haven't been able to import EPS files exported from Sibelius (version 2) into Scribus. The reason is they use Type 3 fonts. See &lt;a href="http://nashi.altmuehlnet.de/pipermail/scribus/2007-September/025501.html"&gt;this message thread&lt;/a&gt; for more information and some possible solutions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-8234768673509948782?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/8234768673509948782/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=8234768673509948782' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/8234768673509948782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/8234768673509948782'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2007/10/why-scribus-fails-to-import-sibelius.html' title='Why Scribus fails to import Sibelius music'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-3565938073117294200</id><published>2007-09-26T18:30:00.001+03:00</published><updated>2007-10-03T20:26:10.979+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pdf python'/><title type='text'>Goodie for your PDF toolbox: automatic cropping tool</title><content type='html'>Ok so I couldn't find a PDF auto-cropping tool for Unix anywhere. Except for a csh shell script which worked fine but which I didn't like too much. So I rolled my sleeves and created my own in Python. Here it is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;#!/usr/bin/python&lt;br /&gt;&lt;br /&gt;import re, sys&lt;br /&gt;from subprocess import Popen, PIPE&lt;br /&gt;&lt;br /&gt;bbsub = re.compile('%%BoundingBox: [\d ]+\n')&lt;br /&gt;&lt;br /&gt;def fixbbox(pdfpath):&lt;br /&gt;badeps = Popen('pdftops -eps %s -' % pdfpath,&lt;br /&gt;               shell=True, stdout=PIPE).stdout.read()&lt;br /&gt;gs = Popen('gs -sDEVICE=bbox -dNOPAUSE -dBATCH -',&lt;br /&gt;           shell=True, stdin=PIPE, stderr=PIPE)&lt;br /&gt;gs.stdin.write(badeps)&lt;br /&gt;gs.stdin.close()&lt;br /&gt;bbox = gs.stderr.read()&lt;br /&gt;goodeps = bbsub.sub(bbox, badeps)&lt;br /&gt;epstopdf = Popen('epstopdf --filter --outfile=%s' % pdfpath,&lt;br /&gt;                 shell=True, stdin=PIPE)&lt;br /&gt;epstopdf.stdin.write(goodeps)&lt;br /&gt;epstopdf.stdin.close()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;for pdfpath in sys.argv[1:]:&lt;br /&gt;    fixbbox(pdfpath)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-3565938073117294200?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/3565938073117294200/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=3565938073117294200' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/3565938073117294200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/3565938073117294200'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2007/09/goodie-for-your-pdf-toolbox-automatic.html' title='Goodie for your PDF toolbox: automatic cropping tool'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-3151975759431047511</id><published>2007-04-14T10:36:00.000+03:00</published><updated>2007-04-14T10:45:05.360+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacs keyboard'/><title type='text'>en-dash and em-dash in Emacs</title><content type='html'>Finally! I found them both! The em-dash can be typed in Emacs (on Ubuntu Edgy) with the key sequence &lt;span style="font-family: courier new;"&gt;Compose - - -&lt;/span&gt; (that's three hyphens), and the en-dash with &lt;span style="font-family: courier new;"&gt;Compose - - .&lt;/span&gt; (two hyphens and a period).&lt;br /&gt;&lt;br /&gt;The en- and em-dashes seem to look identical when using Bitstream Vera Sans Mono, which is annoying but understandable in a monospace font, but at least I can now type them.&lt;br /&gt;&lt;br /&gt;And where's the &lt;span style="font-family: courier new;"&gt;Compose&lt;/span&gt; key, you ask. Well, in addition to other peculiar keyboard customizations, I've re-assigned &lt;span style="font-family: courier new;"&gt;Compose&lt;/span&gt; to the context menu key. On my Dell laptop it's above &lt;span style="font-family: courier new;"&gt;F9&lt;/span&gt;, and on most full-size international keyboards it's next to &lt;span style="font-family: courier new;"&gt;Alt Gr&lt;/span&gt;. The XModmap rule for this trick is &lt;span style="font-family: courier new;"&gt;keycode 117 = Multi_key&lt;span style="font-family: times new roman;"&gt;.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now, if someone fixed copy-pasting extended characters from terminal windows to Emacs... And where's the full list of compose key sequences?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-3151975759431047511?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/3151975759431047511/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=3151975759431047511' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/3151975759431047511'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/3151975759431047511'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2007/04/en-dash-and-em-dash-in-emacs.html' title='en-dash and em-dash in Emacs'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-5843283010292148041</id><published>2007-04-10T00:02:00.000+03:00</published><updated>2007-04-10T00:14:35.779+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='latex'/><title type='text'>LaTeX index entries inside footnotes fail</title><content type='html'>Inside LaTeX footnotes, &lt;span style="font-family: courier new;"&gt;\index&lt;/span&gt; entries which contain special characters or commands may appear multiple times in the index. This can happen when &lt;span style="font-family: courier new;"&gt;\makeidx&lt;/span&gt; inserts extra spaces in the markup which does not happen with &lt;span style="font-family: courier new;"&gt;\index&lt;/span&gt; in normal text.&lt;br /&gt;&lt;br /&gt;I haven't found a good solution nor really understood what's going on. Let this post act as a reminder for myself in case I run into the same problem later and have forgotten details about it. If you know a good solution, please do send a comment!&lt;br /&gt;&lt;br /&gt;Here's a simple test case:&lt;br /&gt;&lt;pre style="font-family: courier new;"&gt;&lt;span style="font-size:85%;"&gt;% do e.g. the following:&lt;br /&gt;% pdflatex test.tex ; makeindex test.idx ; pdflatex test.tex ; evince test.pdf&lt;br /&gt;% and take a close look at test.idx to see where the problem is&lt;br /&gt;&lt;br /&gt;\documentclass{article}&lt;br /&gt;\usepackage{makeidx}&lt;br /&gt;\makeindex&lt;br /&gt;\begin{document}&lt;br /&gt;First index entry is here\index{Index Entry@\emph{Index Entry}}.&lt;br /&gt;\footnote[1]{Second index entry is here\index{Index Entry@\emph{Index Entry}}}&lt;br /&gt;\printindex&lt;br /&gt;\end{document}&lt;/span&gt;&lt;/pre&gt;And here's what ends up in test.idx:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;\indexentry{Index Entry@\emph{Index Entry}}{1}&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;\indexentry{Index Entry@\emph  {Index Entry}}{1}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is how the index then looks like:&lt;br /&gt;&lt;blockquote&gt;Index Entry, 1&lt;br /&gt;Index Entry, 1&lt;br /&gt;&lt;/blockquote&gt;&lt;a href="http://groups.google.com/group/comp.text.tex/browse_thread/thread/fc7c28e876a97278"&gt;Here's some insight&lt;/a&gt; about what is going on. It isn't clear to me what Bernd is suggesting in his "Quick&amp;amp;Dirty" advice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-5843283010292148041?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/5843283010292148041/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=5843283010292148041' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/5843283010292148041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/5843283010292148041'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2007/04/latex-index-entries-inside-footnotes.html' title='LaTeX index entries inside footnotes fail'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-2383151295696702881</id><published>2007-02-28T10:01:00.000+02:00</published><updated>2007-02-28T10:26:43.898+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django newforms'/><title type='text'>Django newforms improvement</title><content type='html'>It's not the first time during my acquaintance with Django that I've needed functionality not available in the forms (and old manipulators) machinery.&lt;br /&gt;&lt;br /&gt;Actually the need is quite simple: Only edit a subset of a model's field in a form, and keep old values for the rest.&lt;br /&gt;&lt;br /&gt;For example, I might have a simplified form for changing only the content of a FlatPage, but keeping the title, url and other properties. In this case I'd like to be able to just say&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-family: courier new;"&gt;FlatPageForm = form_for_instance(&lt;br /&gt;  myflatpage,&lt;br /&gt;  include_fields=['content'])&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;and have Django automagically fill in values from the original &lt;span style="font-family: courier new;"&gt;myflatpage&lt;/span&gt; object when saving form data for all fields except &lt;span style="font-family: courier new;"&gt;content&lt;span style="font-family: times new roman;"&gt;.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There was a &lt;a href="http://simon.bofh.ms/logger/django/2007/02/28/00/43/"&gt;good discussion&lt;/a&gt; about this on the &lt;a href="http://code.djangoproject.com/wiki/IrcFAQ"&gt;#django IRC channel&lt;/a&gt;, and eventually I implemented this functionality based on newforms. The module is available in &lt;a href="http://trac.ambitone.com/ambidjangolib/browser/trunk/newforms_extra/models.py"&gt;our Subversion repository&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-2383151295696702881?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/2383151295696702881/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=2383151295696702881' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/2383151295696702881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/2383151295696702881'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2007/02/django-newforms-improvement.html' title='Django newforms improvement'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-6578661461865788964</id><published>2006-11-30T01:14:00.000+02:00</published><updated>2007-04-10T22:01:13.506+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><title type='text'>Database conversion Django style</title><content type='html'>For various reasons, I wanted to convert one of my Django projects to use SQLite instead of PostgreSQL. After searching for a conversion tool and not finding one, I asked on the #django IRC channel where ubernostrum came up with this plan:&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(175, 127, 0);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-size:100%;"&gt;simple process:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;1) Select everything.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(136, 190, 90);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;2) Pickle it.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(136, 190, 90);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;3) Save to file.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(136, 190, 90);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;4) Read file.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(136, 190, 90);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;5) Unpickle.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(136, 190, 90);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;6) Save to db.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(136, 190, 90);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;:)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(136, 190, 90);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;Or something like that.&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;First I thought it was funny, but then started to think about it and it made perfect sense. Off to coding then.&lt;br /&gt;&lt;br /&gt;(fast forward while I'm writing 11 lines of Python code)&lt;br /&gt;&lt;br /&gt;Yes, that's right, after eleven lines of Python statements I had a script which pickles the whole Django database into a file, loads it back and saves all the objects!&lt;br /&gt;&lt;br /&gt;I actually used more time googling for conversion tools than writing this tool. And since it was so easy, I thought I'll be nice and write some comments, a nice command line interface and sprinkle some debug output for verbose mode. And of course create this blog posting.&lt;br /&gt;&lt;br /&gt;The script is available at our Subversion server. Point your browser or svn client to &lt;a href="http://svn.ambitone.com/ambidjangolib/trunk/dbpickle/dbpickle.py"&gt;our repository&lt;/a&gt; and enjoy.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update:&lt;/span&gt; Now the script handles ForeignKeys as well, and makes sure related objects are saved first. The length of the script went up from 11 lines of course...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update:&lt;/span&gt; I just added many-to-many relation support.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update:&lt;/span&gt; You can now use plugins to hook into the retrieval and saving of objects. This is useful for database schema migration.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-6578661461865788964?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/6578661461865788964/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=6578661461865788964' title='1 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/6578661461865788964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/6578661461865788964'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2006/11/database-conversion-django-style.html' title='Database conversion Django style'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-2781542743082245094</id><published>2006-09-13T09:45:00.000+03:00</published><updated>2006-09-13T10:14:28.072+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tricks'/><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Enjoying the web on GPRS while minimizing traffic (or even off-line), trick #1</title><content type='html'>This summer I spent quite a lot of time at a summer cottage without broadband, but still had to work on the laptop from time to time. I can get on-line with GPRS on my mobile phone, but that's slow and expensive.&lt;br /&gt;&lt;br /&gt;While coding, it's convenient to have on-line documentation and other resources at hand. I could of course download Python, Django and PHP documentation archives and browse them off-line on the hard disk, but often the on-line user comments are very useful, too.&lt;br /&gt;&lt;br /&gt;Enter &lt;a href="http://gertjan.freezope.org/replicator/"&gt;http-replicator&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Http-replicator is a handy Python script which acts as a HTTP proxy and cache. The special thing about it is that it stores all fetched pages as individual files in the filesystem, so it's easy to archive them or clean up some and retain other pages. The files are neatly arranged in directories named after the site domains and organized according to the URLs.&lt;br /&gt;&lt;br /&gt;A nice plus is that it's very easy to sneak-peek at and rip CSS and JavaScript used on visited pages since I can immediately open them in an editor.&lt;br /&gt;&lt;br /&gt;I keep http-replicator running in the background and run &lt;a href="http://www.getfirefox.com/"&gt;Firefox&lt;/a&gt; with a separate profile which uses the local http-replicator as proxy by default.&lt;br /&gt;&lt;br /&gt;You can also run http-replicator in "static mode" so files that are   present are served from cache directly without contacting the   server. I use this to minimize traffic, but I specify some dynamic sites like on-line banking and webmail as exceptions in the Firefox proxy settings.&lt;br /&gt;&lt;br /&gt;Here's my script on Linux for launching both http-replicator and Firefox with the special profile:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;/usr/bin/http-replicator -p 8086 -d /home/akaihola/http-cache -s --daemon --pid=/tmp/http-replicator.pid&lt;br /&gt;MOZ_NO_REMOTE=1 /usr/bin/firefox -P cachefox&lt;br /&gt;kill `cat /tmp/http-replicator.pid`&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The "cachefox" profile simply sets Firefox to use localhost:8086 as the HTTP proxy. I've symlinked all files from my regular Firefox profile directory to the cachefox profile directory exept &lt;code&gt;prefs.js&lt;/code&gt; so I don't have to worry about synchronizing bookmarks or extensions between the profiles.&lt;br /&gt;&lt;br /&gt;I use this setup exclusively when on GPRS, and if I know beforehand that I'm going to need some pages, I run it already when still on broadband and simply visit them all. It's also possible to slurp whole sites with &lt;code&gt;http_proxy="http://localhost:8086" &lt;a href="http://www.gnu.org/software/wget/"&gt;wget&lt;/a&gt; --delete-after -r http://www.site.domain/&lt;/code&gt; (I haven't tested this yet though).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-2781542743082245094?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/2781542743082245094/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=2781542743082245094' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/2781542743082245094'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/2781542743082245094'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2006/09/enjoying-web-on-gprs-while-minimizing.html' title='Enjoying the web on GPRS while minimizing traffic (or even off-line), trick #1'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-5832479804056829900</id><published>2006-09-07T18:51:00.000+03:00</published><updated>2006-09-07T19:00:04.851+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bugs'/><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='podcast'/><title type='text'>Found use for podcasts today</title><content type='html'>I know podcasts are no new thing anymore, but my first chance to try them only came with my new smartphone. It's a very good way to entertain myself while walking a sleeping baby in the park.&lt;br /&gt;&lt;br /&gt;As always with new toys, it just doesn't work without technical problems. For some reason &lt;a href="http://symbianoggplay.sourceforge.net/"&gt;OggPlay&lt;/a&gt; (S60, Nokia 6680) won't continue playback of the &lt;a href="http://blogs.s60.com/voiceofs60/"&gt;Voice of S60&lt;/a&gt; podcast (MP3) after rewinding or fast forwarding. I already made a &lt;a href="http://sourceforge.net/forum/message.php?msg_id=3903532"&gt;note&lt;/a&gt; of the problem to the OggPlay forums, so let's see...&lt;a href="http://blogs.s60.com/voiceofs60/"&gt;&lt;/a&gt;&lt;span class="down" style="display: block;" id="formatbar_CreateLink" title="Link" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"&gt;&lt;/span&gt;&lt;br /&gt;Now I just have to find the most interesting podcasts and a handy way to transfer them from my Linux laptop to the phone.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-5832479804056829900?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/5832479804056829900/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=5832479804056829900' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/5832479804056829900'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/5832479804056829900'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2006/09/found-use-for-podcasts-today.html' title='Found use for podcasts today'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-5264142089617391166</id><published>2006-09-03T17:03:00.000+03:00</published><updated>2006-09-03T17:34:08.072+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='email'/><category scheme='http://www.blogger.com/atom/ns#' term='thunderbird'/><title type='text'>Tags, Thunderbird and Dovecot</title><content type='html'>The newest snapshots of the Mozilla Thunderbird e-mail client have at last tag support. It's possible to tag messages with multiple keywords and do live searches based on the tags à la gmail.&lt;br /&gt;&lt;br /&gt;There's just still something wrong ­- if I have a message tagged as "To Do" and try to remove all tags and re-tag it with another tag, all my changes are reverted after a moment. Maybe a problem with the Dovecot IMAP server? Oh well, instead of writing I should check bugzilla and maybe download a newer nightly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-5264142089617391166?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/5264142089617391166/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=5264142089617391166' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/5264142089617391166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/5264142089617391166'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2006/09/tags-thunderbird-and-dovecot.html' title='Tags, Thunderbird and Dovecot'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2752288660688812438.post-1234000095027648149</id><published>2006-08-18T15:46:00.000+03:00</published><updated>2006-08-18T15:58:49.499+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web frameworks'/><category scheme='http://www.blogger.com/atom/ns#' term='cakephp'/><title type='text'>Choosing a PHP web framework</title><content type='html'>A client required their website to be done in PHP.  Not being a great fan of the language since I discovered Python (and Django), I didn't like the idea too much. But it has to be done, so I set off to see what new tools have been created since I left the PHP world.&lt;br /&gt;&lt;br /&gt;Based on other people's comments and without really trying out the many web frameworks available, I narrowed down my choices to Symfony, CakePHP and CodeIgniter. They seemed to represent the best of the breed for complete, medium and lightweight solutions, consecutively.&lt;br /&gt;&lt;br /&gt;None of the PHP frameworks seemed to be as mature as Django, and from example code snippets the clumsiness of the language showed clearly compared to Python. But the frameworks seem to be well on their way and offer great benefits compared to bare-bones PHP programming (which I've done my share of in this life, thank you).&lt;br /&gt;&lt;br /&gt;Anyway, among the three "finalists", CodeIgniter seemed to lack some essential features I need, namely user authentication and access control lists. Symfony is praised for its documentation but said to be big and difficult. Many good reviews have been written about CakePHP, so I started to work with it.&lt;br /&gt;&lt;br /&gt;I'll write more about my impressions on CakePHP soon&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2752288660688812438-1234000095027648149?l=akaihola.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akaihola.blogspot.com/feeds/1234000095027648149/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2752288660688812438&amp;postID=1234000095027648149' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/1234000095027648149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2752288660688812438/posts/default/1234000095027648149'/><link rel='alternate' type='text/html' href='http://akaihola.blogspot.com/2006/08/choosing-php-web-framework.html' title='Choosing a PHP web framework'/><author><name>akaihola</name><uri>http://www.blogger.com/profile/15705799059863673635</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
