Showing posts with label Meta. Show all posts
Showing posts with label Meta. Show all posts

Friday, August 21, 2009

Switched to Zine

Update: Since writing this post, I've moved the blog to Blogger. Turns out, I'm not fond of system administration. Besides, Blogger has shiny gadgets, widgets and whatnots.

I’ve moved the blog to Zine and to a different server. Zine is quite nice. I’d like to see a plugin that implements one of the common APIs so that I can use a blogging client like ScribeFire. After installing this ReST plugin I was able to migrate the (little) content from my semi-homegrown blog into Zine. The import from an atom feed didn’t work for me for some reason.
The old blog was down quite a bit. When I noticed that the other day I had a look and found out what the problem was. There was a cron job, running imapfilter to filter my mails directly on the mail servers, so they would be filed correctly in all clients on all machines I use. Among those mail servers are those from Google. Apparently Google had some problems with their SSL certificates, since running imapfilter from the shell produced the following output:
Server certificate subject: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
Server certificate issuer: /C=US/O=Google Inc/CN=Google Internet Authority
Server key fingerprint: 35:D1:0A:42:F3:FE:61:4E:CD:0C:02:05:D1:CC:D9:52
ATTENTION: SSL/TLS certificate fingerprint mismatch.
Proceed with the connection (y/n)?
Sigh. So there were hundreds of imapfilter processes sitting there, waiting for input. The CPU was maxed out, but sorting by CPU load in top didn’t identify the culprit. No process was using much CPU time by itself. Death by scheduling. So, Google: Fix your SSL certificates. Or was it a mitm attack…? Of course, it couldn't possibly be my fault, right? ;)

Friday, March 20, 2009

First Post!

Update: Since writing this post, I've moved the blog to Blogger. Turns out, I'm not fond of system administration. Besides, Blogger has shiny gadgets, widgets and whatnots.

So I decided to try this newfangled blogging thing that everyone is talking about. Maybe it's not just a fad after all. At this adoption rate expect me to start using twitter in about five years. ;)

In good internet tradition the first post is completely self-referential, describing what software I used to set this blog up.


Tools

One of the coolest features of the Django framework is its support for reusable applications. The concept of a generic foreign key is crucial for this to work, as well as some conventions like naming url patterns and adding a parameter for template names to view functions. By using some existing reusable apps, I was able to create this blog in a short amount of time while still having more flexibility for modification and extension than using a shrink-wrapped blog app like wordpress would provide. It seems to be a rite of passage for django programmers to write their own blog. I find the idea of doing that to be boring and pointless. Luckily, there's basic-apps. Since there are so many blogs built with Django out there I stole everything that seemed useful from them, like syntax highlighting and the help text for comment formatting.

Development and deployment

Using virtualenv I created a bootstrap script with an after_install function that installs pip and fetches some code from svn repositories.
import os, subprocess
def after_install(options, home_dir):
     subprocess.call([join(home_dir, 'bin', 'easy_install'), 'pip'])
     src = join(home_dir, 'src')
     if not os.path.exists(src):
         os.makedirs(src)
     curdir = os.getcwd()
     os.chdir('src')
     subprocess.call(['svn', 'co', 
      'http://django-basic-apps.googlecode.com/svn/trunk/', 'basic'])
     subprocess.call(['svn', 'co',
      'http://django-trackback.googlecode.com/svn/trunk/', 'django-trackback'])
     os.chdir(curdir)
     target = join(curdir, 'src', 'basic')
     link = join(curdir, 'lib', 'python2.5', 'site-packages', 'basic')
     os.symlink(target, link)
     target = join(curdir, 'src', 'django-trackback', 'trackback')
     link = join(curdir, 'lib', 'python2.5', 'site-packages', 'trackback')
     os.symlink(target, link)

Calling svn in a subprocess and symlinking to site-packages is pretty kludgy. Normally those repository URLs belong in the requirements.txt file. But basic-apps and django-trackback were missing setup.py files when I checked out the source. Maybe pip has a way to deal with that. Maybe I'll get around to look for this in the docs some day.

Speaking of pip, here's how my requirements.txt file looks like. I use rope for code completion in vim. Along the same line, ipython, django-extensions and Werkzeug are useful for development and debugging, but not really neccessary for running the blog.
flup
ipython
rope
markdown
docutils
BeautifulSoup
Werkzeug
pygments
-e svn+http://django-tagging.googlecode.com/svn/trunk/#egg=django-tagging
-e svn+http://code.djangoproject.com/svn/django/trunk/#egg=django-trunk
-e git+git://github.com/django-extensions/django-extensions.git#egg=django-extensions

After creating those files I created a virtualenv and ran pip to install the requirements.
$ mkdir myblog && cd myblog
$ python myblog-boot.py --no-site-packages .
[...]
$ source bin/activate
$ pip install -r requirements.txt


Django project

To glue all this goodness together I performed the following steps.

  • Create django project
  • $ django-admin startproject myblog
  • Edit settings.py, add to INSTALLED_APPS
'django.contrib.admin',<br />'django.contrib.markup',<br />'django.contrib.comments',<br />'django_extensions',<br />'tagging',<br />'basic.inlines',<br />'basic.blog',<br />'trackback',<br />

Here's my urls.py

# -*- coding: UTF-8 -*-
# vim: set fileencoding: utf-8

from django.conf import settings
from django.conf.urls.defaults import *
from django.contrib import admin
from basic.blog import views as blog_views
from basic.blog.feeds import BlogPostsFeed
from basic.blog.feeds import BlogPostsByCategory
from feeds import AllCommentsFeed
from feeds import AtomAllCommentsFeed
from feeds import AtomBlogPostsFeed
from feeds import AtomBlogPostsByCategory
from feeds import AtomCommentsForEntryFeed
from feeds import CommentsForEntryFeed
import views

admin.autodiscover()

rss_feeds = {
    'entries': BlogPostsFeed,
    'full-entries': BlogPostsFeed,
    'categories': BlogPostsByCategory,
    'entry-comments': CommentsForEntryFeed,
    'comments': AllCommentsFeed,
}

atom_feeds = {
    'entries': AtomBlogPostsFeed,
    'full-entries': AtomBlogPostsFeed,
    'categories': AtomBlogPostsByCategory,
    'entry-comments': AtomCommentsForEntryFeed,
    'comments': AtomAllCommentsFeed,
}

urlpatterns = patterns('',
    url(r'^(?P\d{4})/(?P\w{3})/(?P\d{1,2})/(?P[-\w]+)/$',
        view=blog_views.post_detail,
        name='blog_detail'),

    url(r'^(?P\d{4})/(?P\w{3})/(?P\d{1,2})/$',
        view=blog_views.post_archive_day,
        name='blog_archive_day'),

    url(r'^(?P\d{4})/(?P\w{3})/$',
        view=blog_views.post_archive_month,
        name='blog_archive_month'),

    url(r'^(?P\d{4})/$',
        view=blog_views.post_archive_year,
        name='blog_archive_year'),

    url('^$',
        view=blog_views.post_list,
        name='blog_index'),

    url('^archive/$',
        view=views.archive_list,
        name='archive_list'),

    url(r'^categories/(?P[-\w]+)/$',
        view=blog_views.category_detail,
        name='blog_category_detail'),

    url (r'^categories/$',
        view=blog_views.category_list,
        name='blog_category_list'),

    url (r'^search/$',
        view=blog_views.search,
        name='blog_search'),

    url(r'^page/(?P\w)/$',
        view=blog_views.post_list,
        name='blog_index_paginated'),

    url(r'^ping/', include('trackback.urls')),

    (r'^rss/(?P.*)/$', 'django.contrib.syndication.views.feed',
        {'feed_dict': rss_feeds}),

    (r'^atom/(?P.*)/$', 'django.contrib.syndication.views.feed',
        {'feed_dict': atom_feeds}),

    (r'^comments/', include('django.contrib.comments.urls')),

    (r'^admin/doc/', include('django.contrib.admindocs.urls')),
    (r'^admin/(.*)', admin.site.root),

    (r'^dev/random/$', views.randomize),

    (r'^pygments_lexers/$', views.pygments_lexers),
)

I wrote three little view functions of my own, but didn't bother creating an app for them. One is a primitive archive page, the second is a list of all lexers that pygments has installed and the last one is /dev/random. It will return either 4 or NINE NINE NINE NINE NINE NINE. I've linked to stackoverflow so you can see that I even stole that idea somewhere. But at least I improved it by adding the second value. It's actually using the python random module making it a true random device. ;)

For now I can only receive trackbacks/pingbacks, not send them. And I haven't really tried that, so it probably doesn't work either. I'll worry about that later, when there is some evidence that someone is actually reading this blog. The same strategy will be used for dealing with comment spam. ;)

Webserver Configuration

  • lighttpd
  • FastCGI
  • Hint: You might want to add FORCE_SCRIPT_NAME = "" to your settings.py.
TODO:

Yeah, I kinda got lazy in the end. ;) Anyway, that's it. Let's see whether I can come up with something more interesting for the next post.