Public calendars on Google Calendar

August 8, 2009
0 comments Misc. links

Public calendars on Google Calendar I've been looking for something like this but thought I had to find an external VCAL file to import into my Google Calendar. Apparently Google Calendar has a whole bunch of useful public calendars. For example, UK Holidays.

Public calendars on Google Calendar To enable any of these click the little down arrow next to the word "Add" on the left hand side under the list of all calendars you have enabled.

The rest is easy and needs no explanation. I did however spot one "bug" or perhaps I just confused myself. When I went in the second time to take a screenshot of the list of public calendars available it had mistakenly unselected the ones I selected a few seconds ago. Perhaps a caching problem.

More optimization of Peterbe.com - CSS sprites

August 5, 2009
0 comments Web development

I have now made the menu images on this site into a CSS sprite. Basically, instead of loading 6 different images totaling 10Kb it now only needs 1 image which is 7Kb! The difference in filesize isn't the big win here but the reduced number of requests is the big deal.

The number one tip from the Yahoo! Performance people is reducing the number of requests and this is what I've done.

To generate the sprite image I use the CSS Sprite Generator and then fiddled it a bit to make it work for this site. Thanks guys!

The major drawbacks of CSS Sprite images isn't really technical but it's just that it's an optimization hack. The next time I need to change any of the images I have to reapply the hack and there's a risk that by the time I need to get back into it I will have forgotten how to do it or where to go to do it. Anyway, I'm really pleased with the performance of this site now.

The 4-hour Work Week by Timothy Ferris

July 29, 2009
0 comments Books

An American businessman took a vacation to a small coastal Mexican village on doctor's orders. Unable to sleep after an urgent phone call from the office the first morning, he walked out to the pier to clear his head. A small boat with just one fisherman had docked, and inside the boat were several large yellowfin tuna. The American complimented the Mexican on the quality of his fish.
  "How long did it take you to catch them?" the American asked.
  "Only a little while," the Mexican replied in surprisingly good English.
  "Why don't you stay out longer and catch more fish?" the American then asked.
  "I have enough to support my family and give a few to friends," the Mexican said as he unloaded them into a basket.
  "But... What do you do with the rest of your time?"
  The Mexican looked up and smiled. "I sleep late, fish a little, play with my children, take a siesta with my wife, Julia, and stroll into the village each evening, where I sip wine and play guitar with my amigos. I have a full and busy life, senor."
  The American laughed and stool tall. "Sir, I'm a Harvard M.B.A. and can help you. You should spend more time fishing, and with the proceeds, buy a bigger boat. In no time, you could buy several boats with the increased haul. Eventually, you would have a fleet of fishing boats."
  He continued, "Instead of selling your catch to a middleman, you would sell directly to the consumers, eventually opening your own cannery. You would control the product, processing, and distribution, and move to Mexico City, then to Los Angeles, and eventually New York City, where you could run your expanding enterprise with proper management."
  The Mexican fisherman asked, "But senor, how long will all this take?"
  To which the American replied, "15-20 years. 25 tops."
  "But what then, senor?"
  The American laughed and said, "That's the best part. When the time is right, you would announce an IPO and sell your company stock to the public and become very rich. You would make millions."
  "Millions, senor? Then what?"
  "Then you would retire and move to a small coastal fishing village, where you would sleep late, fish a little, play with your kids, take a siesta with your wife, and stroll to the village in the evenings where you could sip wine and play your guitar with your amigos..."
(The 4-hour Work Week, Timothy Ferriss, page 231-232)

The 4-hour Work Week by Timothy Ferris This quote is ripped from a book called The 4-hour Work Week by nutty young entrepreneurial American man called Timothy Ferriss. Just finished the book I have to admit being quite hit by it. It says on the back "WARNING: Don't read this book if you don't want to change your life". I don't want to radically change my life but a bit wouldn't hurt.

If your an office guy working for the man (or as the Japanese call it "salary men") reading this book will probably leave a bad taste of guilt and trembling eager to take his advice on board.

My take is to not take the book on at either 100% (what the author wants) or 0% (what almost all readers will do) but instead do some cherry picking of ideas and concepts that I like. I've actually already started to change a few things in my life all thanks to inspiration in the book.

Battery life usage on an iPhone 3G(S)

July 27, 2009
0 comments iPhone

Batter life usage on an iPhone 3G(S) There's an app in the App Store called myBatteryLife which is cute. It shows you how much time you have left on your iPhone battery depending on what you do. It's missing basic standby time, variable to brightness level and it doesn't say anything about GPS but it does make it fairly obvious some basic facts.

A) You can listen to a lot of music before the battery runs out. This was NOT the case with my previous Nokia N95

B) 3G uses approximately twice as much power as Wi-Fi. (What about WPA/WEP encrypted WiFi networks?)

C) Watching a movies is half as power thirsty as surfing the web.

I listen to news podcasts and Last.fm in the morning when cycling to work so clearly I know what not to do if the charging stations are far apart.

Judging by how rounded these figure are I guess it's fairly guesstimates. I'm currently running a Powerlog and once that file's gotten fat and levelled I'll butcher it with some scripting and try to come up with something more realistic and accurate. Watch this space.

gorun.py - Using (py)inotify to run commands when files change

July 20, 2009
0 comments Python

gorun.py - Using (py)inotify to run commands when files change By popular demand I've made my little pyinotify wrapper available for download. It's nothing fancy really but damn useful and productive.

It relies on inotify (so you're stuffed on OSX and Windows) which makes it very fast and efficient (as opposed to periodic polling and file modification time comparisons).

At the moment it's actually quite generic for any command and any file but I'm hoping to take this to the next level with some magic dust that automatically only runs unit tests that fail or something. We'll see what happens.

Truncated! Read the rest by clicking the link below.

Getting uploadify to work

July 17, 2009
11 comments Web development

Uploadify is a great tool. I'm using it so that people can mass upload photos for a photo gallery app I'm working on. This afternoon I've spent about 3-4 hours trying to get it to work on Windows (IE, Chrome, Firefox). It wasn't easy but I think I got it working in the end.

Pitfall no 1 (easy)

The Javascript code that "wraps" the "uploader.swf" had a bug in how it calculated the installed Flash version in IE (only tested in IE6). So I fixed that and will shortly send a patch to the uploadify.com.

Pitfall no 2 (hard)

I've got nginx on the server passing requests to a Django daemon via FCGI. For some obscure reason, on Windows, if the file uploader.swf was served as a static file directly from nginx it wouldn't work and I would get 403 errors that are never explained in the nginx error log or anything. So I changed it to let Django serve up the uploader.swf file which obviously sets headers differently and then it worked.

Headers when served directly by nginx:


HTTP/1.1 200 OK
Server: nginx/0.6.34
Date: Fri, 17 Jul 2009 17:01:09 GMT
Content-Type: application/x-shockwave-flash
Content-Length: 18659
Last-Modified: Wed, 08 Jul 2009 12:29:52 GMT
Connection: keep-alive
Accept-Ranges: bytes

Headers when served from Django (django.views.static.serve):


HTTP/1.1 200 OK
Server: nginx/0.6.34
Date: Fri, 17 Jul 2009 17:01:03 GMT
Content-Type: application/x-shockwave-flash
Connection: keep-alive
Last-Modified: Wed, 08 Jul 2009 12:29:52 GMT
Content-Length: 18659
Vary: Cookie

Pitfall no 3 (super complex)

It was working in Firefox Linux, Safari Mac, Firefox Mac but not IE6 Windows, Firefox Windows or Chrome Windows. This despite having the latest (and same as Linux) version of Flash (10.0.22.x)!

Solution: In Internet Explorer, click on Options and delete the cookies, temporary Internet files and Clear History (for good measure). Now it works!!

It's been a hell of a journey but now it's crossed. Let's hope I can save some other poor sucker a couple of hours by making this blog available.

UPDATE

ow it's stopped working again. Works in Opera Windows but not IE 8 in windows 7. Grr...!

UPDATE 2

I've completely abandoned Uploadify now and instead gone for Swfupload which actually works really well.

setuptools usability - not good, what can be done?

July 15, 2009
12 comments Python

Gun to your head; what would it take to make setuptools as a package author easy to use?

I've spent far too long time today trying to create a package for a little piece of code I've written. Because I can never remember all the bizarre options and commands to setup.py I tried to do it by following Tarek Ziade's wonderful Expert Python Programming but I still got stuck.

Granted, I did not read the f**n manual. Why should I have to? I've got more important things to do such as eating cookies and watching tv.

Truncated! Read the rest by clicking the link below.

premailer.py - Transform CSS into line style attributes with lxml.html

July 11, 2009
9 comments Python

By blogging about it I can pretty much guarantee that someone will comment and say "Hey, why didn't you use the pypi/alreadyexists package which does the same thing but better". I couldn't find one after a quick search and I felt the hacker mood creeping up on my begging me to (re)invent it.

premailer.py takes a HTML page, finds all CSS blocks and transforms these into style attributes. For example, from this:


<html>
  <head>
    <title>Test</title>
    <style>
    h1, h2 { color:red; }
    strong {
      text-decoration:none
    }
    </style>
  </head>
  <body>
    <h1>Hi!</h1>
    <p><strong>Yes!</strong></p>
  </body>
</html>

You get this:


<html>
  <head>
    <title>Test</title>
  </head>
  <body>
    <h1 style="color:red">Hi!</h1>
    <p><strong style="text-decoration:none">Yes!</strong></p>
  </body>
</html>

Why is this useful? When you're writing HTML emails. Like this newsletter app that I'm working on.

I just wrote it late yesterday and it needs lots of work to impress but for the moment it works for me. If I take the time to tidy it up properly I'll turn it into a package. Assuming there isn't one already :)

UPDATE

No available on github.com and as a PyPi package

UPDATE #2

Two new copy-cats have been released:

  • python-premailer which seems to do the same thing but without lxml (which is sort of the whole point)
  • inline-styler which also uses lxml but I don't know what it does differently or better

Might be worth poking around at these if my premailer isn't good enough.

My first iPhone web app - Crosstips iPhone interface

July 1, 2009
2 comments iPhone

My first iPhone web app - Crosstips iPhone interface I've just finished my first fully iPhone enable web app (not to be confused with iPhone app which are installed onto the phone via App Store). It's here:

crosstips.org/iphone

It's basically a wrapped version of Crosstips that uses these sample resources to imitate a native app but in the Safari web browser.

It works really well and wasn't too hard to do. I think the key is to remember that the iPhone Safari is after all a web browser but it also has excellent support for AJAX (and jQuery). There are probably some bugs that I have spotted yet and there is work on the optimization but for now I'm happy.

So, if you like to solve crosswords in bed and you have an iPhone then bookmark crosstips.org/iphone

My dislike for booleans and that impact on the Django Admin

June 1, 2009
8 comments Django

I've got this model in Django:


class MyModel(models.Model):
   completed_date = models.DateTimeField(null=True)

My dislike for booleans and that impact on the Django Admin By using a DateTimeField instead of a BooleanField I'm able to record if an instance is completed or not and when it was completed. A very common pattern in relational applications. Booleans are brief but often insufficient. (Check out Ned Batchelder's Booleans suck)

To make it a bit more convenient (and readable) to work with I added this method:


class MyModel(models.Model):
   completed_date = models.DateTimeField(null=True)

   @property
   def completed(self):
       return self.completed_date is not None

That's great! Now I can do this (use your imagination now):


>>> from myapp.models import MyModel
>>> instance = MyModel.objects.all()[0]
>>> instance.completed
False
>>> instance.completed_date = datetime.datetime.now()
>>> instance.save()
>>> instance.completed
True

I guess I could add a setter too.

But Django's QuerySet machinery doesn't really tie in with the ORM Python classes until the last step so you can't use these property methods in your filtering/excluding. What I want to do is to be able to do this:


>>> from myapp.models import MyModel
>>> completed_instances = MyModel.objects.filter(completed=True)
>>> incomplete_instances = MyModel.objects.filter(completed=False)

To be able to do that I had to add special manager which is sensitive to the parameters it gets and changes them on the fly. So, the manager plus model now looks like this:


class SpecialManager(models.Manager):
   """turn certain booleanesque parameters into date parameters"""

   def filter(self, *args, **kwargs):
       self.__transform_kwargs(kwargs)
       return super(SpecialManager, self).filter(*args, **kwargs)

   def exclude(self, *args, **kwargs):
       self.__transform_kwargs(kwargs)
       return super(SpecialManager, self).exclude(*args, **kwargs)

   def __transform_kwargs(self, kwargs):
       bool_name, date_name = 'completed', 'completed_date'
       for key, value in kwargs.items():
           if bool_name == key or key.startswith('%s__' % bool_name):
               if kwargs.pop(key):
                   kwargs['%s__lte' % date_name] = datetime.now()
               else:
                   kwargs[date_name] = None

class MyModel(models.Model):
   completed_date = models.DateTimeField(null=True)

   @property
   def completed(self):
       return self.completed_date is not None

Now, that's fine but there's one problem. For the application in hand, we're relying on the admin interface a lot. Because of the handy @property decorator I set on the method completed() I now can't include completed into the admin's list_display so I have to do this special trick:


class MyModelAdmin(admin.ModelAdmin):
   list_display = ('is_completed',)

   def is_completed(self, object_):
       return object_.completed
   is_completed.short_description = u'Completed?'
   is_completed.boolean = True

Now, I get the same nice effect in the admin view where this appears as a boolean. The information is still there about when it was completed if I need to extract that for other bits and pieces such as an advanced view or auditing. Pleased!

Now one last challenge with the Django admin interface was how to filter on these non-database-fields? It's been deliberately done so that you can't filter on methods but it's slowly changing and with some hope it'll be in Django 1.2. But I'm not interested in making my application depend on a patch to django.contrib but I really want to filter in the admin. We've already added some custom links and widgets to the admin interface.

After a lot of poking around and hacking together with my colleague Bruno Renié we came up with the following solution:


class MyModelAdmin(admin.ModelAdmin):
   list_display = ('is_completed',)

   def is_completed(self, object_):
       return object_.completed
   is_arrived.short_description = u'Completed?'
   is_arrived.boolean = True

   def changelist_view(self, request, extra_context=None, **kwargs):
       from django.contrib.admin.views.main import ChangeList
       cl = ChangeList(request, self.model, list(self.list_display),
                       self.list_display_links, self.list_filter,
                       self.date_hierarchy, self.search_fields, 
                       self.list_select_related,
                       self.list_per_page,
                       self.list_editable, self)
       cl.formset = None

       if extra_context is None:
           extra_context = {}

       if kwargs.get('only_completed'):
           cl.result_list = cl.result_list.exclude(completed_date=None)
           extra_context['extra_filter'] = "Only completed ones"

       extra_context['cl'] = cl
       return super(SendinRequestAdmin, self).\
         changelist_view(request, extra_context=extra_context)

   def get_urls(self):
       from django.conf.urls.defaults import patterns, url
       urls = super(SendinRequestAdmin, self).get_urls()
       my_urls = patterns('',
               url(r'^only-completed/$', 
                   self.admin_site.admin_view(self.changelist_view),
                    {'only_completed':True}, name="changelist_view"),
       )
       return my_urls + urls

Granted, we're not getting the nice filter widget on the right hand side in the admin interface this time but it's good enough for me to be able to make a special link to /admin/myapp/mymodel/only-completed/ and it works just like a normal filter.

Ticket 5833 is quite busy and has been going on for a while. It feels a daunting task to dig in and contribute when so many people are already ahead of me. By writing this blog entry hopefully it will help other people who're hacking on their Django admin interfaces who, like me, hate booleans.