<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-us"><title>Taylan Pince - Blog Posts</title><link href="http://www.taylanpince.com/blog/" rel="alternate" /><id>http://www.taylanpince.com/blog/</id><updated>2009-08-29T03:05:39-05:00</updated><author><name>Taylan Pince</name></author><subtitle>Latest blog posts from taylanpince.com</subtitle><link rel="self" href="http://feeds.feedburner.com/taylanpince-posts" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry><title>Movie Genie on Sale</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/nXzKzdt_LCA/" rel="alternate" /><updated>2009-08-29T03:05:39-05:00</updated><id>http://www.taylanpince.com/blog/posts/movie-genie-on-sale/</id><summary type="html">&lt;p&gt;To celebrate its 10,000th download, Movie Genie will be on sale until Monday, August 31st. Get it 50% off if you haven&amp;#8217;t yet!
&lt;/p&gt;



&lt;p&gt;&lt;a href="http://moviegenieapp.com/" rel="nofollow"&gt;Visit the bookmark &amp;raquo;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/nXzKzdt_LCA" height="1" width="1"/&gt;</summary><category term="iphone" /><category term="moviegenie" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/movie-genie-on-sale/</feedburner:origLink></entry><entry><title>Movie Genie and Big Picture Updated</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/MLDp8mV8qbk/" rel="alternate" /><updated>2009-07-23T09:34:24-05:00</updated><id>http://www.taylanpince.com/blog/posts/movie-genie-and-big-picture-updated/</id><summary type="html">&lt;p&gt;Updates for Movie Genie and Big Picture have been approved by Apple yesterday evening. While Big Picture only got a small bug fix release, Movie Genie 1.2.2 adds a number of new features that people have been waiting for:
&lt;/p&gt;
&lt;ul&gt;
 &lt;li&gt;
     Episode listings for TV series
 &lt;/li&gt;

 &lt;li&gt;
     Award details, categorized by year
 &lt;/li&gt;

 &lt;li&gt;
     Quotes
 &lt;/li&gt;

 &lt;li&gt;
     User Reviews
 &lt;/li&gt;

 &lt;li&gt;
     Full Synopsis
 &lt;/li&gt;

 &lt;li&gt;
     Certifications
 &lt;/li&gt;

 &lt;li&gt;
     Parental Guides
 &lt;/li&gt;

 &lt;li&gt;
     Extended cast &amp;amp; crew data
 &lt;/li&gt;

 &lt;li&gt;
     Improved navigation interface
 &lt;/li&gt;

 &lt;li&gt;
     Optimized performance
 &lt;/li&gt;

 &lt;li&gt;
     Alphabetical sorting for favorites
 &lt;/li&gt;

 &lt;li&gt;
     Improved connection reliability
 &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I am very excited about these new abilities of the app. For more info, head on over to &lt;a href="http://hippofoundry.com"&gt;Hippo Foundry&lt;/a&gt;.
&lt;/p&gt;



&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/MLDp8mV8qbk" height="1" width="1"/&gt;</summary><category term="hippofoundry" /><category term="iphone" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/movie-genie-and-big-picture-updated/</feedburner:origLink></entry><entry><title>Django, Push Notifications, and Prowl</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/dIrNs-kWOxc/" rel="alternate" /><updated>2009-07-17T13:41:18-05:00</updated><id>http://www.taylanpince.com/blog/posts/django-push-notifications-and-prowl/</id><summary type="html">&lt;p&gt;One issue that I have had with this blog for a while has been the lack of 
   notifications when a new, legitimate comment is posted under one of my 
   articles. I don&amp;#8217;t get many comments, but when I do, I would like to be able 
   to respond right away. I don&amp;#8217;t like email notifications, so when 
   &lt;a href="https://prowl.weks.net/"&gt;Prowl&lt;/a&gt; was announced this week, I was excited about 
   the possibility of sending push notifications to my iPhone whenever a new 
   comment was posted. It turned out to be much easier than I anticipated, 
   thanks to Jacob Burch&amp;#8217;s &lt;a href="http://github.com/jacobb/prowlpy/tree/master"&gt;Prowlpy&lt;/a&gt; 
   module and Django&amp;#8217;s signals framework.
&lt;/p&gt;

&lt;p&gt;To setup push notifications for anything, you need to get these done first:
&lt;/p&gt;
&lt;ol&gt;
 &lt;li&gt;&lt;p&gt;Register for an account at the &lt;a href="https://prowl.weks.net/"&gt;Prowl&lt;/a&gt; site, then
      go to iTunes Store and buy Prowl for your iPhone/iPod Touch
&lt;/p&gt;

 &lt;/li&gt;

 &lt;li&gt;&lt;p&gt;After setting up your device, login to your account at the Prowl web site,
      click on Settings, and generate an API key
&lt;/p&gt;

 &lt;/li&gt;

 &lt;li&gt;&lt;p&gt;Download and install &lt;a href="http://github.com/jacobb/prowlpy/tree/master"&gt;Prowlpy&lt;/a&gt; 
      and &lt;a href="http://code.google.com/p/httplib2/"&gt;httplib2&lt;/a&gt;. I opted for simply 
      putting Prowlpy somewhere on my PYTHONPATH, and installing httplib2 through
      aptitude:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install python-httplib2
&lt;/code&gt;&lt;/pre&gt;
 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that you are ready, simply create a signals.py file under whichever Django 
   application you would like to use to send push notifications for, and use 
   something similar to this for sending out messages to the Prowl API:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;br /&gt;    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.sites.models&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Site&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;prowl.prowlpy&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Prowl&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;comment_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;        Connects to the Prowl API to send a push notification&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="n"&gt;prowl_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Prowl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROWL_API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prowl_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify_key&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;br /&gt;                &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;                    &lt;span class="n"&gt;prowl_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;br /&gt;                        &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;taylanpince.com&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;                        &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;New Comment&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;                        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;A new comment has been published under blog post: &lt;/span&gt;&lt;span class="si"&gt;%(title)s&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="si"&gt;%(link)s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br /&gt;                            &lt;span class="s"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;                            &lt;span class="s"&gt;&amp;quot;link&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;http://&lt;/span&gt;&lt;span class="si"&gt;%(domain)s%(path)s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br /&gt;                                &lt;span class="s"&gt;&amp;quot;domain&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;                                &lt;span class="s"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_absolute_url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;br /&gt;                            &lt;span class="p"&gt;}&lt;/span&gt;&lt;br /&gt;                        &lt;span class="p"&gt;}&lt;/span&gt;&lt;br /&gt;                    &lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;                    &lt;span class="k"&gt;pass&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;Obviously, you also need to put your Prowl API key that you got from your 
   account page earlier inside your settings.py file:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;    &lt;span class="n"&gt;PROWL_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;YOUR_API_KEY&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;Finally, you have to link this signal to a model. In your models.py file, you 
   can have the model call this method as a pre_save signal:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models.signals&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pre_save&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;blog.signals&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;comment_notification&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;br /&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;      &lt;span class="o"&gt;...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class="n"&gt;pre_save&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comment_notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;You could, of course, call this signal post_save as well. That&amp;#8217;s up to you. In 
   my case, I also do some spam filtering through the &lt;a href="http://akismet.com/"&gt;Akismet&lt;/a&gt; 
   API, so it makes more sense to handle all of that in a pre_save signal and 
   only send out a notification if a comment passes that check. I suggest you do 
   something similar - it wouldn&amp;#8217;t be fun to get push notifications for spam.
&lt;/p&gt;
&lt;p&gt;Finally, I had to fork the Prawlpy script and make some minor modifications to 
   make sure the content is encoded properly. Unless you comment out the following 
   lines in Prowlpy, you will get urlencoded strings in your notification:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;    &lt;span class="c"&gt;# URL-encode and string-ify keywords. Better type/content testing is needed here&lt;/span&gt;&lt;br /&gt;    &lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;br /&gt;    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;br /&gt;    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;br /&gt;    &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;And that&amp;#8217;s it, now I will always be on top of the comments posted here!
&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/dIrNs-kWOxc" height="1" width="1"/&gt;</summary><category term="django" /><category term="iphone" /><category term="python" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/django-push-notifications-and-prowl/</feedburner:origLink></entry><entry><title>App Store Review Frustrations</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/CPwUrQERA9g/" rel="alternate" /><updated>2009-07-17T09:48:51-05:00</updated><id>http://www.taylanpince.com/blog/posts/app-store-review-frustrations/</id><summary type="html">&lt;p&gt;Two days ago, &lt;a href="http://hippofoundry.com/movie-genie"&gt;Movie Genie&lt;/a&gt;&amp;#8217;s main server, which provides all search results for the application, went down for about six hours, making it pretty much unusable to anyone. This was a dumb mistake on my part, since the issue was very easy to resolve, but I should have had the necessary precautions in place to warn me of the server downtime. I do have those running now, but it&amp;#8217;s too late, and within those six hours, Movie Genie&amp;#8217;s average rating went down from &lt;strong&gt;a full 5 stars to 2 or 3&lt;/strong&gt; in most countries.
&lt;/p&gt;

&lt;p&gt;This is, obviously, frustrating for me. I can&amp;#8217;t blame the users who wrote those reviews. It&amp;#8217;s human nature to not praise someone when they do a good job, but blame them the moment they fail. Movie Genie was downloaded by almost 3,000 people before this happened, and I had a total of thirty reviews from around the world, all of them ranging between 3 to 5 stars. While the servers were down, I got around &lt;strong&gt;fifteen 1-star reviews&lt;/strong&gt;. It doesn&amp;#8217;t matter that the app is working properly now, sale figures were &lt;strong&gt;down 50%&lt;/strong&gt; yesterday. I don&amp;#8217;t expect them to go up anytime soon.
&lt;/p&gt;
&lt;p&gt;My real frustration, though, is with &lt;strong&gt;Apple and the iTunes Store reviews&lt;/strong&gt;. Customer satisfaction has been the most important thing for me since I started selling on the store, exactly a month ago. I replied to every email within an hour, tried to solve every issue, give everyone who cared to contact me the amount of time and interest they deserved. Unfortunately, with these reviews, I have &lt;strong&gt;no way of contacting&lt;/strong&gt; my frustrated customers. I wish I could just send them an email and say: &amp;#8220;Please, just try the app one more time&amp;#8221;. But they are just nicknames on the iTunes Store with no contact information, and they probably will never launch the app again.
&lt;/p&gt;
&lt;p&gt;Even if they did, the process for modifying or deleting a review is so convoluted that they would never bother to take those unfair reviews back. I had a client who gave me a 1-star review contact me later. I convinced him that the problem was resolved, and he apologized for his unfair assessment. He told me that he emailed Apple so they can take down his review. I told him that Apple would never take his review down for him, that he had to do it himself, and gave him a step by step guide on how he can. He never responded back, and his review is still there.
&lt;/p&gt;
&lt;p&gt;So, there you have it. I am just frustrated, and I am not going to look at the Store rankings or reviews for a while. I&amp;#8217;ll just keep my head down, focus on building new apps.
&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/CPwUrQERA9g" height="1" width="1"/&gt;</summary><category term="apple" /><category term="iphone" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/app-store-review-frustrations/</feedburner:origLink></entry><entry><title>Big Picture now available on the App Store</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/S1ePQm_CGu8/" rel="alternate" /><updated>2009-07-15T14:37:32-05:00</updated><id>http://www.taylanpince.com/blog/posts/big-picture-now-available-on-the-app-store/</id><summary type="html">&lt;p&gt;This is just a quick note to announce that my second iPhone app, Big Picture, has just been approved by Apple. This is a simple but elegant client for boston.com&amp;#8217;s &lt;a href="http://www.boston.com/bigpicture/"&gt;The Big Picture&lt;/a&gt; blog. To learn more about the app, follow the link.
&lt;/p&gt;



&lt;p&gt;&lt;a href="http://hippofoundry.com/big-picture/" rel="nofollow"&gt;Visit the bookmark &amp;raquo;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/S1ePQm_CGu8" height="1" width="1"/&gt;</summary><category term="bigpicture" /><category term="iphone" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/big-picture-now-available-on-the-app-store/</feedburner:origLink></entry><entry><title>Oggifier: A Simple GUI Wrapper for ffmpeg2theora</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/_z6Of_nJ0sE/" rel="alternate" /><updated>2009-07-03T19:07:43-05:00</updated><id>http://www.taylanpince.com/blog/posts/oggifier-gui-wrapper-ffmpeg2theora/</id><summary type="html">&lt;p&gt;With the release of Firefox 3.5 earlier this week, the discussion around HTML5 and its native support for video and audio tags suddenly gained speed. One of the major issues around these new features is the lack of proper video codecs that can be used. Apple&amp;#8217;s Safari supports H.264, but Firefox and other open source
   browsers can&amp;#8217;t, due to licensing problems. Their adopted open source format, OGG, seems like the way to go, but it&amp;#8217;s not that easy to generate, especially if you are on a Mac.
&lt;/p&gt;
&lt;p&gt;Yesterday, John Gruber from Daring Fireball &lt;a href="http://daringfireball.net/2009/07/ffmpeg2theora"&gt;published a lengthy post&lt;/a&gt; about possible solutions for OGG conversion on Mac, and his suggestion was to use Jan Gerber&amp;#8217;s excellent &lt;a href="http://v2v.cc/~j/ffmpeg2theora/"&gt;ffmpeg2theora&lt;/a&gt;. I thought I would spend some time and build a simple GUI wrapper for this tool so it would be more easily adopted by Mac users. Today I am releasing an early beta version of Oggifier, a simple GUI wrapper around ffmpeg2theora.
&lt;/p&gt;

&lt;img src="http://appcast.taylanpince.com/oggifier/badge.png" alt="Oggifier" /&gt;

&lt;p&gt;Apart from a clean and simple user interface, Oggifier provides two additional features that might be useful to people:
&lt;/p&gt;
&lt;ul&gt;
 &lt;li&gt;&lt;p&gt;It comes bundled with the latest version of ffmpeg2theora, so you don&amp;#8217;t have to deal with installing it (even though Jan Gerber&amp;#8217;s package installer is perfect, dragging and dropping an app that handles everything for you is easier)
&lt;/p&gt;

 &lt;/li&gt;

 &lt;li&gt;&lt;p&gt;Oggifier supports automated updates through Andy Matuschak&amp;#8217;s &lt;a href="http://sparkle.andymatuschak.org/"&gt;Sparkle framework&lt;/a&gt;, which means that you will receive future updates to both Oggifier and the ffmpeg2theora automatically
&lt;/p&gt;

 &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can get &lt;a href="http://appcast.taylanpince.com/oggifier/Oggifier.0.3.0.zip"&gt;Oggifier 0.3.0 here&lt;/a&gt;. Please let me know what you think in the comments. I am planning to add support for advanced ffmpeg2theora options within the next few weeks.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I just published version 0.3.0 that adds a simple preferences pane with support for automated conversion and optionally quitting after the process is complete.
&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/_z6Of_nJ0sE" height="1" width="1"/&gt;</summary><category term="mac" /><category term="objectivec" /><category term="oggifier" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/oggifier-gui-wrapper-ffmpeg2theora/</feedburner:origLink></entry><entry><title>Movie Genie is now available on the App Store!</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/StVxFufDwGg/" rel="alternate" /><updated>2009-06-16T10:08:53-05:00</updated><id>http://www.taylanpince.com/blog/posts/movie-genie-is-now-available-on-the-app-store/</id><summary type="html">&lt;p&gt;This is just a quick post to let everyone know that Apple has finally approved &lt;a href="http://moviegenieapp.com"&gt;Movie Genie&lt;/a&gt; yesterday night and it is now available on the &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=318807631&amp;amp;mt=8"&gt;iTunes Store&lt;/a&gt;. Contrary to what I expected, the approval process was painless, and Apple did a very good job of reviewing the app for possible defects.
&lt;/p&gt;
&lt;p&gt;I hope that people will enjoy this first app, I am very excited to be building on this platform. If you have any suggestions or comments, feel free to email me or leave your thoughts here.
&lt;/p&gt;



&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/StVxFufDwGg" height="1" width="1"/&gt;</summary><category term="iphone" /><category term="moviegenie" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/movie-genie-is-now-available-on-the-app-store/</feedburner:origLink></entry><entry><title>Movie Genie is Ready for Beta Testing</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/mmiILnQNZ3g/" rel="alternate" /><updated>2009-05-13T15:44:00-05:00</updated><id>http://www.taylanpince.com/blog/posts/movie-genie-is-ready-for-beta-testing/</id><summary type="html">&lt;p&gt;I haven&amp;#8217;t been blogging much lately, but I do have a good excuse this time. Last month, I decided to finally start learning Objective-C and getting into iPhone &amp;amp; Mac development. Being a Mac user for 18 years now, developing on the platform has been one of my dreams.
&lt;/p&gt;
&lt;p&gt;Today, I am happy to announce my first iPhone app, &lt;a href="http://moviegenieapp.com"&gt;Movie Genie&lt;/a&gt; - a mobile IMDb client. It&amp;#8217;s open for beta testing at this point, and I welcome everyone interested in trying it out and giving feedback.
&lt;/p&gt;



&lt;p&gt;&lt;a href="http://moviegenieapp.com/" rel="nofollow"&gt;Visit the bookmark &amp;raquo;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/mmiILnQNZ3g" height="1" width="1"/&gt;</summary><category term="iphone" /><category term="moviegenie" /><category term="objectivec" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/movie-genie-is-ready-for-beta-testing/</feedburner:origLink></entry><entry><title>Easier Translations with django-rosetta</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/w3FU6syEJDo/" rel="alternate" /><updated>2009-04-08T11:27:39-05:00</updated><id>http://www.taylanpince.com/blog/posts/easier-translations-with-django-rosetta/</id><summary type="html">&lt;p&gt;I just found out about this Django application called &lt;a href="http://code.google.com/p/django-rosetta/"&gt;django-rosetta&lt;/a&gt; that uses the &lt;a href="http://code.google.com/apis/ajaxlanguage/"&gt;Google AJAX Language API&lt;/a&gt; to suggest translations for translatable strings in your project. Even more interesting is the admin-like interface it creates to enter translations directly from a browser - and it doesn&amp;#8217;t need any database tables, everything is directly written to translation files. I didn&amp;#8217;t try it out yet, but if it works as advertised, it could become one of my default applications.
&lt;/p&gt;



&lt;p&gt;&lt;a href="http://code.google.com/p/django-rosetta/" rel="nofollow"&gt;Visit the bookmark &amp;raquo;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/w3FU6syEJDo" height="1" width="1"/&gt;</summary><category term="django" /><category term="i18n" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/easier-translations-with-django-rosetta/</feedburner:origLink></entry><entry><title>Django Toronto Meetups</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/35QJbx8RpWY/" rel="alternate" /><updated>2009-04-02T22:57:22-05:00</updated><id>http://www.taylanpince.com/blog/posts/django-toronto-meetups/</id><summary type="html">&lt;p&gt;Tonight was the latest in a series of get togethers among Django developers in the Toronto area. We have been doing these for the past few months, since October 2008, and even though our numbers haven&amp;#8217;t grown much, I am pretty sure there are other Djangonauts in Toronto who would be interested. We are planning to do monthly scheduled meetings with proper presentations at the &lt;a href="http://linuxcaffe.ca/"&gt;Linux Caffe&lt;/a&gt; first Wednesday of every month. So join the &lt;a href="http://groups.google.com/group/django-toronto"&gt;mailing list&lt;/a&gt; and let&amp;#8217;s start organizing!
&lt;/p&gt;



&lt;p&gt;&lt;a href="http://groups.google.com/group/django-toronto" rel="nofollow"&gt;Visit the bookmark &amp;raquo;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/35QJbx8RpWY" height="1" width="1"/&gt;</summary><category term="django" /><category term="toronto" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/django-toronto-meetups/</feedburner:origLink></entry><entry><title>Schema Migrations with South</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/c9eZnr_URkE/" rel="alternate" /><updated>2009-04-02T11:26:03-05:00</updated><id>http://www.taylanpince.com/blog/posts/schema-migrations-with-south/</id><summary type="html">&lt;p&gt;For the past few months, I have been trying out the different schema migration tools for Django. At first, the obvious choice seemed like &lt;a href="http://code.google.com/p/django-evolution/"&gt;django-evolution&lt;/a&gt;. It was the oldest and the most mature project, and after watching the &lt;a href="http://www.youtube.com/watch?v=VSq8m00p1FM"&gt;schema evolution panel&lt;/a&gt; from DjangoCon 2008, it was also the one that made most sense in terms of architecture. After working with django-evolution in a few projects though, I found that it did not work well in practice. I have recently been testing &lt;a href="http://south.aeracode.org/"&gt;South&lt;/a&gt;, and it seems like the better choice.
&lt;/p&gt;

&lt;p&gt;I had a few big issues with django-evolution:
&lt;/p&gt;
&lt;ul&gt;
 &lt;li&gt;
     Inability to go backwards or forwards, or to be able to skip migrations (or mutations as it calls them)
 &lt;/li&gt;

 &lt;li&gt;
     Inability to use custom fields that require additional parameters
 &lt;/li&gt;

 &lt;li&gt;
     Lack of integration with syncdb (if someone in the team accidentally does a syncdb, it fails)
 &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I love how clever evolution can be when guessing the modifications to a model, but I don&amp;#8217;t think what&amp;#8217;s needed from a migration tool is cleverness. I needed a tool that gave me the freedom to do whatever I wanted, while making my job easier by managing all the changes.
&lt;/p&gt;
&lt;p&gt;I have been playing around with South for the past week, and it seems like it offers pretty much everything I need, plus a bit more. My only grief with it is the lack of documentation. I think they should stop focusing on adding any more features to the project and write a solid documentation with tutorials instead.
&lt;/p&gt;
&lt;p&gt;One of the best features of South is that it doesn&amp;#8217;t need to be on a project from the very beginning, it works on a per-app level, and can be attached to an app at any given time. You simply generate an initial migration by calling the startmigration command. Let&amp;#8217;s say we have an app named blog:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;./manage.py startmigration blog --initial&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;This will create a migrations directory inside the application, and the next time you call syncdb, South will prevent Django from synchronizing this application, since it is now marked as an app managed by South. In order to apply the migration, you will have to call migrate. If this is a new app that&amp;#8217;s not in the database yet, we can simply do this:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;./manage.py migrate&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;If this is an old app that was already synchronized by using syncdb, then we have to mark this migration as &amp;#8220;applied&amp;#8221; without actually applying it:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;./manage.py migrate blog --fake&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;Let&amp;#8217;s say we added a new model under the blog app called Article, we can once again call startmigration to generate it for us:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;./manage.py startmigration blog add_article_model --model Article&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;What if we forgot to add the slug field to the model we just created? Let&amp;#8217;s 
   generate the migration for adding that field:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;./manage.py startmigration blog add_article_slug --add-field Article.slug&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;We could also modify the default migration generated by South to populate
   that slug field. Here is a quick example:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;br /&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template.defaultfilters&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;slugify&lt;/span&gt;&lt;br /&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.translation&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ugettext_lazy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;south.db&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;blog.models&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forwards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;blog_article&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;slug&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SlugField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Slug&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;br /&gt;        &lt;br /&gt;        &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;br /&gt;        &lt;br /&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backwards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;blog_article&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;slug&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;blog_article&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;slug&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;There are a few interesting bits here. First of all, we make sure the slugify
   method is imported so we can generate the slugs, but we also import the lazy
   translation method, ugettext_lazy because it&amp;#8217;s being imported with a custom
   name.
&lt;/p&gt;
&lt;p&gt;In the forwards method, we make sure that the slug field has a default 
   value, otherwise PostgreSQL will complain about a NOT NULL field being created
   without values. After creating the column in the database, we loop through
   all articles and generate slugs for them.
&lt;/p&gt;
&lt;p&gt;In the backwards method, there is an additional call to delete_index. This is
   required to remove the index on the slug field, South does not add this
   automatically.
&lt;/p&gt;
&lt;p&gt;Now we can migrate:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;./manage.py migrate blog 0003&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;And that&amp;#8217;s it! So far, I really like the freedom and ease of use South is giving me.
&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/c9eZnr_URkE" height="1" width="1"/&gt;</summary><category term="django" /><category term="python" /><category term="south" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/schema-migrations-with-south/</feedburner:origLink></entry><entry><title>Django and mod_wsgi</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/wDKGHN92SHc/" rel="alternate" /><updated>2009-04-01T09:10:44-05:00</updated><id>http://www.taylanpince.com/blog/posts/django-and-mod_wsgi/</id><summary type="html">&lt;p&gt;Finally, Django&amp;#8217;s recommended deployment method switched from mod_python to mod_wsgi. Updated docs are now online.
&lt;/p&gt;



&lt;p&gt;&lt;a href="http://docs.djangoproject.com/en/dev/howto/deployment/modwsgi/" rel="nofollow"&gt;Visit the bookmark &amp;raquo;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/wDKGHN92SHc" height="1" width="1"/&gt;</summary><category term="apache" /><category term="django" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/django-and-mod_wsgi/</feedburner:origLink></entry><entry><title>Periodic Table of Typefaces</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/hpvIbfKZ_DY/" rel="alternate" /><updated>2009-03-30T09:39:17-05:00</updated><id>http://www.taylanpince.com/blog/posts/periodic-table-of-typefaces/</id><summary type="html">&lt;p&gt;I love this - a list of popular typefaces grouped categorically in a periodic table format!
&lt;/p&gt;



&lt;p&gt;&lt;a href="http://www.behance.net/Gallery/Periodic-Table-of-Typefaces/193759" rel="nofollow"&gt;Visit the bookmark &amp;raquo;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/hpvIbfKZ_DY" height="1" width="1"/&gt;</summary><category term="design" /><category term="typography" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/periodic-table-of-typefaces/</feedburner:origLink></entry><entry><title>sorl.thumbnail and Custom Processors</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/UnK6_0nT8QM/" rel="alternate" /><updated>2009-03-27T10:54:24-05:00</updated><id>http://www.taylanpince.com/blog/posts/sorlthumbnail-and-custom-processors/</id><summary type="html">&lt;p&gt;In the past few months, &lt;a href="http://code.google.com/p/sorl-thumbnail/"&gt;sorl.thumbnail&lt;/a&gt; has become one of the standard libraries I always include in my Django projects. It&amp;#8217;s unobstrusive and very easy to implement into templates, but my one grief with it has been the lack of capability for doing some custom processing on images. As it turns out, sorl actually allows custom processors to be added. When I found out about this feature, I set out to write some for my current project.
&lt;/p&gt;

&lt;p&gt;If you haven&amp;#8217;t heard about the processors functionality, it&amp;#8217;s all in the &lt;a href="http://sorl-thumbnail.googlecode.com/svn/trunk/sorl/thumbnail/README.TXT"&gt;documentation&lt;/a&gt;. I am not sure how I missed it before. Implementing is pretty easy, first you have to define a THUMBNAIL_PROCESSORS variable in your settings.py file:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;THUMBNAIL_PROCESSORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class="s"&gt;&amp;#39;sorl.thumbnail.processors.colorspace&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="s"&gt;&amp;#39;sorl.thumbnail.processors.autocrop&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="s"&gt;&amp;#39;sorl.thumbnail.processors.scale_and_crop&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="s"&gt;&amp;#39;sorl.thumbnail.processors.filters&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class="s"&gt;&amp;#39;imagery.processors.pad&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="s"&gt;&amp;#39;imagery.processors.round&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;The first four are obviously sorl&amp;#8217;s own default processors. The last two are my custom ones. &amp;#8220;pad&amp;#8221; allows resizing without cropping, while keeping the final image dimensions always the same. So basically it centers the resized image in an area and fills the rest of the image with a given background colour:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PIL&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class="n"&gt;THUMBNAIL_PADDING_COLOUR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requested_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    Adds padding around the image to match the requested_size&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;pad&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;requested_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;RGB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requested_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;THUMBNAIL_PADDING_COLOUR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;        &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;requested_size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;requested_size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;        &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paste&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;        &lt;span class="n"&gt;im&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valid_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pad&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;The other one, &amp;#8220;round&amp;#8221;, is a bit more complex. Basically I needed a processor to add rounded corners to an image by pasting a mask on top. Since it&amp;#8217;s not possible to pass parameters to a processor, I ended up having a method that can take one of a few rounding options and use the actual name of the option to find the correct mask file:
&lt;/p&gt;
&lt;div class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;ROUND_VALID_OPTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class="s"&gt;&amp;quot;round&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="s"&gt;&amp;quot;round-box&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="s"&gt;&amp;quot;round-wide&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requested_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    Adds rounded corners around the image&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;mask_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ROUND_VALID_OPTIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;mask_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mask_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;THUMBNAIL_ROUND_MASKS_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.png&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;mask_type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;        &lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paste&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valid_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROUND_VALID_OPTIONS&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;

&lt;p&gt;This makes sorl indispensable for me!
&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/UnK6_0nT8QM" height="1" width="1"/&gt;</summary><category term="django" /><category term="python" /><category term="sorl" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/sorlthumbnail-and-custom-processors/</feedburner:origLink></entry><entry><title>Running Safari 4 Beta with Safari 3</title><link href="http://feedproxy.google.com/~r/taylanpince-posts/~3/08VYeWw6lAU/" rel="alternate" /><updated>2009-03-25T14:37:30-05:00</updated><id>http://www.taylanpince.com/blog/posts/running-safari-4-beta-with-safari/</id><summary type="html">&lt;p&gt;When Apple announced the &lt;a href="http://www.apple.com/safari/download/"&gt;Safari 4 Beta&lt;/a&gt;, I was itching to try it out right away, but I knew that if I installed it, the current WebKit would get wiped out my system. This might be acceptable for most, but I would like to keep testing my sites against Safari 3 at least for another year before moving on. So I waited until someone came up with a solution to install them side by side. &lt;a href="http://brian.mastenbrook.net/display/29"&gt;Brian Mastenbrook&amp;#8217;s package extractor&lt;/a&gt; seems like exactly what I need, it works very well for me so far.
&lt;/p&gt;



&lt;p&gt;&lt;a href="http://brian.mastenbrook.net/display/29" rel="nofollow"&gt;Visit the bookmark &amp;raquo;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/taylanpince-posts/~4/08VYeWw6lAU" height="1" width="1"/&gt;</summary><category term="apple" /><category term="webkit" /><feedburner:origLink>http://www.taylanpince.com/blog/posts/running-safari-4-beta-with-safari/</feedburner:origLink></entry></feed>
