07/18: A date to remember

Mark the 18th of July as an important date on your calendar. I certainly have. Let it be remembered as the day that you finally update your feeds and subscribe to my blog over at LiveJournal. (Posted on both LiveJournal and Blogger)

Goodbye, Blogger

Having used Blogger for a while, I've come to hate its interface. Therefore, I'm going to use LiveJournal for a while. My new blog address is kurtmckee.livejournal.com. Remember to subscribe to my new Livejournal feed!

Python's time module

So I've been working like a fiend to import all of my comments from around the internet. It has been a herculean effort because almost everything has to be done manually. One comment here, another there, and no uniform way to extract those comments.

Two sites, however, made comment retrieval easy: Xanga and Slashdot. Both offer fine lists of my old comments. I first scraped Xanga for all of my old comments, but stumbled when converting the dates and times of my comments into a usable format: while Xanga outputs dates and times in the form 1/1/2006 1:22 AM, I need output in the form 2006-01-01 01:22:00 for import into the comment-tracking software I'm writing.

I hacked together some trivial regular expressions and had a solution in about ten minutes. However, I then wondered if there was software that could convert the dates and times for me. Surprise, it's already included in Python! Python's time module includes a function called 'strptime' that does all of the heavy interpretation and standardization of the formats. With that in mind, it'll be no trouble converting Slashdot's comments' dates and times (like Sunday January 01 2006, @01:22AM)!

The moral of the story is, do a little searching before writing your own software; somebody else may have already written a solution for you.

Of molds and such

Once again it's summer in Evanston, and the humidity is wreaking havoc on my perishables. Today I discovered that a uncut pineapple, a pomelo, and a loaf of bread were consumed by mold. Last summer I suffered greatly due to the humidity, including expedited milk expirations, and the loss of potatoes and carrots.

Carrots don't mold, my mother informed me.

That has not been my experience. So, can anyone out there recommend a good dehumidifier?

Hadouken!

Okay, SO, I'm totally throwing myself a party in my honor, and to add pride to pretentiousness, I want everyone to check out the invitation and mark whether you're coming or not so I know how much to pat myself on the back for a party well-thrown.


My birthday party on April 26th went over extremely well. The people meshed well and we had a wonderful time. It all started with Aaron and Allan arriving shortly after 7:00a. We ate at Chipotle, picked up a volleyball, and wandered around campus before heading to the beach.


Desolation


Bow and arrow (Tokuda)


Library (Tokuda)


Laughter (Tokuda)

At the beach, we played foxtail, flew a kite, and when enough people arrived, we played volleyball for a couple of hours. That was a good time, and an inside joke from that time is the source from which the title of this entry originated.


Flight (Tokuda)

As evening came, it was decided that we should go check out the big pile of dirt that was arbitrarily located at the south end of the beach. More fun ensued (primarily for the guys, but we eventually convinced the girls to join us).


Jump


Geronimo (Tsang)


Doubletake (Tsang)


Lemmings (Tsang)


With help (Tsang)


Hug (Tsang)

Some of my crew left, and we headed to Andrea's apartment for pizza. Unfortunately, we experienced a significant delay getting from the first floor to the third floor.

Videos forthcoming, but Youtube is throwing errors right now

Update - There will be no videos for a while; my cell phone is no longer generating videos with playable audio. I mean, there's audio, but no free software nor service supports the audio codec, called QCELP. Thanks for NOTHING, software patents.

Until there are videos, I'll just explain that the elevator was incorrectly rated up to 2,000 pounds, and it ground to a halt just before reaching the third floor. We were rescued after about 45 minutes.



Saved (Tsang)

Eventually we got out of the elevator; the firemen took down our names and told us not to collectively weigh more than 2000 pounds ever again, although we're certain that 10 people, each weighing less than 200 pounds, shouldn't have caused the elevator to stall.

Pizza was awesome, Lock was awesome, and then we went to Prairie Moon. This birthday party was exactly the experience I needed. Thanks everyone!

A group shot of the remnant prior to Prairie Moon:


Huddle (Tokuda)

"Bow and arrow", "Library", "Laughter", "Flight", and "Huddle" are copyright 2008 Allan Tokuda. Used with permission.

"Geronimo", "Doubletake", "Lemmings", "With Help", "Hug", and "Saved" are copyright 2008 Andrea Tsang. Used with permission.

Going for broke

Yesterday, while at Sheil, Tim called out Kurt! and flashed some kind of hand gesture at me.

Tim: There, see? I just put a curse on you.
Me: Oh, hexes are the worst. Can you...is that something I can just wash out?
Tim: No, you're stuck with it.
Me: Shoot, and I got the Black Spot last week, too.
Tim: You know I'm from Salem, where all the witches were? Yeah, from Salem. You could go there and get some potions and stuff to lift the curse. It'll cost you, though.
Me: So, you haven't really given me a curse, but a financial burden.
Tim: That's the curse.

F-Spot problems

I know I've been promising to upload photos and videos from my birthday, but I've been running into a roadblock that I don't have any good way to deal with. Consequently, I've had no incentive to organize and select the best photos to upload.

For over a year I've been running an old version of F-Spot -- version 0.4.0 -- because I've not had access to the internet in order to get updates. I now have internet access, and have updated F-Spot to version 0.4.2. In the intervening time, however, F-Spot has changed significantly: where previously it stored my photos' timestamps in Universal Coordinated Time (previously called Greenwich Mean Time) and interpreted the actual date and time by subtracting hours from the time, I'm now finding that the new version of F-Spot believes that the timestamps are now in local time. As a result, all of my photos' timestamps are wrong by five and six hours.

Needless to say, I'm flipping out, and my stress is exacerbated by the fact that F-Spot has had timestamp problems for several years. Of course, my solution right now is to make sure that I force my new photos' timestamps forward by five hours. If it's going to be wrong, I want it to be consistently wrong, so that it can be fixed consistently in the future.

Sigh.

Possibly-related bug reports: 340899, 340903, 332025

Stay strong!

The other day a mother came into the toy store with her young son, Nicolas. Her son was very well-behaved, and played quietly at the train table when she asked him to. My boss commented how pleasant the boy was, and she informed us that he had been having an awful day, and that this was the first time he had been content.

When it came time to leave, however, her son threw a fit and began disagreeing with everything his mother said. It was an amusing psychological response.

Come on, it's time to go to Mandy's house.
I don't want to go to Mandy's house!
But she's having a birthday party, and you'll get to have ice cream!
I don't want to have ice cream!
And you'll see all of your friends there!
I don't want to see all of my friends there!

And so on. She put him in his stroller and I held the door open for them, encouraging poor Nicolas as he continued crying and carrying on.

Stay strong, Nicolas!
I don't want to stay strong! he wailed.

Theftable

Discussing with Allan the possibility of him bringing his small desktop computer.

Allan: I don't know if I should bring it...it's kind of...
Me: Theftable?
Allan: "THEFTABLE". Good use of bad English.

Cherokee 0.6.0 and Django

In this post, I'll detail how to set up Cherokee with Django using FastCGI.

I'll be working under the following assumptions:

  • Python, Django, and flup are properly installed on your system (I'm currently running Python 2.5.1, Django 0.96.1, and flup 1.0)
  • You don't have root, and/or want to run Cherokee only in a development environment
So then, let's begin!

Installing Cherokee

In a terminal, change to the directory to which you downloaded Cherokee 0.6.0, and run these commands to install Cherokee in ~/apps:
    $ tar -xzf cherokee-0.6.0.tar.gz
$ cd cherokee-0.6.0/
$ ./configure --prefix=$HOME/apps
$ make
$ make install
$ mkdir ~/apps/var/log
$ mkdir ~/apps/var/run
We made those two directories at the end so that Cherokee can write to access and error logs, and start and stop Cherokee from our web browser.

Starting a Django project

We'll need a Django project to work with. Let's create one in ~/project.
    $ cd ~
$ django-admin.py startproject project
Couldn't be simpler. Of course, if you already have a Django project, take note of its path (and especially the path leading up to the project; in our case we need to remember the path of our home directory: /home/user).

Configuring Cherokee


Alright, time to try out the new Cherokee admin console!
    $ ~/apps/sbin/cherokee-admin
Now open a web browser and go to http://localhost:9090/. You'll see something like this:



Let's first customize the settings in the "General" page:
  • Port: 8080 (must be above 1023, since we don't have root)
  • Port TLS: 8443 (in case we set up HTTPS later)
Click "Submit". Note that this does not save our changes to Cherokee's configuration file! This merely saves our changes temporarily. You must click "Apply" to permanently save your changes.

Next, open the "Virtual Servers" page:



Cherokee requires that the "default" virtual server (or "vserver") always exist as a fall back, but it is possible to create other virtual servers. Thus, you could create a second vserver, bound to a particular domain (like "beta.kurtmckee.org"), that functions under a completely separate set of rules. We won't explore that here, but I recommend exploring it further. For now, let's focus on customizing the "default" vserver. First click on its name in the "Virtual Server List".

You'll be presented with six different sections, starting with "Basics". Let's skip directly to "Behavior":



You see that there are a number of rules already defined for us. By default, Cherokee is configured to run PHP applications like Wordpress or Gallery, but we want a comprehensive Django-based solution! Be sure to study the default rules before continuing, as these rules are very informative, and you'll surely want to customize things later (perhaps by running other software alongside your Django project). For instance, you should notice the "Priority" column -- the larger the number, the sooner the rule is applied.

OK, so go ahead and delete every rule except the first (the '/' directory rule, priority 1). Then, click on the '/' link (it's very small). You'll be presented with this screen:



Change the "Handler" to "FastCGI", and you'll be presented with this screen:



Make absolutely sure to uncheck "Check file"! I have yet to figure out why, but if this is left checked, the variables that your Django application will see will be seriously b0rked up. You should also check "Error handler", which will prevent Cherokee from intercepting the pretty Django debug output and replacing it with terse HTTP error codes. Next, enter the following for the "Host" and "Interpreter":
  • Host: 127.0.0.1:8070 (or whatever port you want, but remember that port)
  • Interpreter: django-admin.py runfcgi host=localhost port=8070 --pythonpath=/home/user

There are two things to note here. Number one, if you set the "Interpreter", Cherokee will be able to automatically start the Django FastCGI server if it's not already running. This is a Good Thing (TM). Second, remember how I told you to keep note of the path to your Django project? You need to change the "pythonpath" argument to the "Interpreter". Since we created the Django project in /home/user/project/, we entered /home/user. Change the argument
accordingly, and then click "Add".

After clicking add, you will see a new line which will allow you to set environment variables for the "Host" and "Interpreter" you just entered. Enter these values:
  • Variable: DJANGO_SETTINGS_MODULE
  • Value: project.settings
Click "Add" at the end of the line. Then, click "Submit Query". You'll see something similar to this screen:



Now click "Apply", which will save our changes to the Cherokee configuration file. You will be taken back to the Cherokee admin welcome screen, which will inform you that the server is not running. Click "Launch" and cross your fingers, because we're about to test our setup! Assuming you see the following screen, then Cherokee is now listening on localhost, port 8080.



So let's visit our new Django project, which is running at http://localhost:8080/! If everything goes well, you should be presented with the following screen:



Congratulations, you've got a functioning Django FastCGI server connected to your brand-new Cherokee installation. There's just one last thing, which I'll leave as a screenshot-less exercise for the reader: when you enable Django's built-in administration, you'll find that things aren't working so well. That will be because Django is requesting media from a (surprise) /media URL. Cherokee is in turn passing this /media URL right back to Django, which doesn't help us at all. You'll need to add a second rule to the "default" vserver with a higher priority than the '/' directory rule. Here's the relevant information:
  • Entry: /media
  • Type: Directory (already selected)
  • Handler: List & Send (already selected)
  • Priority: 100 (or 2, or anything greater than the '/' directory rule's priority)
You'll click "Submit Query" and then edit your new rule, changing the "Document Root" to the Django admin media directory on your filesystem (in my case, that directory is located at /usr/lib/python2.5/site-packages/django/contrib/admin/media/.

Prince of Peace


Prince of Peace

At the 2005 Crusade men's retreat

Announcing Tricycle

When Mat Honan's website Barack Obama is your new bicycle launched, it spawned a number of copycat and parody websites. I thought, "Hey, there might be people who don't have the ability to write their own software, but would enjoy having their own is-your-new-bicycle site." Thus, I wrote Tricycle.

Tricycle is a simple way for anyone to host their own "is your new bicycle" site. Just download and copy index.php into your directory. Modify the settings and you'll be all set.

Tricycle is licensed under the GNU AGPL version 3, so it is 100% free software. Check it out!

Update - Check out Tricycle in action over at Dave Woods is your new bicycle!

Unit testing

I just committed basic unit testing support to Renquist, implemented using Python's unittest framework. The results were quite pleasing. Now I can quickly run multiple automated tests to ensure correct behavior and avoid regressions! Huzzah!

For my first two tests I decided to try using a blank file as both a Feed and as a List. The blank Feed test uncovered a bug I hadn't considered, while the blank List test made me fix a problem I had already tagged as a failure point in the comments of the source code. Both bugs are fixed now, but there's surely still a lot of code that isn't being touched by these tests. But how can I identify those untested lines of code?

The first thing I thought after writing those two test cases was, There should be a way to visually identify the untested code. After looking through the Python documentation, the first thing I found was the "trace" module, which can output every single line that gets run. It would take almost no work at all to generate a very simple HTML file, highlighting tested code (or untested code).

Before I write that software, though, I'm going to search a little online. I can't imagine that someone else hasn't had that idea, too.

Here's to automated testing!

Authorship and extensibility

Feed items can now have authors associated with them in the database using a one-to-many authors-to-items relationship. Renquist by default will only use a one-to-one relationship, however (which means that there will be one author entry for each item entry). At first I struggled with the idea that there would be significant data duplication; I'm the only one writing on this blog, so why store ten identical "Kurt" authors given ten feed items? Wouldn't it make more sense to store that information only once?

Yes, but then I considered the case of comment feeds. I know already that there have been three Davids who have posted on my site. One uses "Dave", another "David", and the third showed up once and called himself "David (a different one)". There could easily be a collision of names, and it would be foolish to think that just because someone says that he's "David" that he's the same "David" who posted three other comments. Make sense?

Therefore I'm choosing to leave it up to someone else (through an as-yet-unrealized plugin framework) to decide how best to minimize duplication. Maybe the plugin could merely minimize duplication by name; easy, but perhaps not ideal in all circumstances. Maybe the plugin could minimize duplication using more advanced means (those three Davids might be writing in three separate languages, for instance). There are at least two other methods that jump to mind, but it's easy to see the potential for a smart plugin.

In the meantime, however, Renquist will store author information despite likely data duplication.

Renquist and distutils

There have been some big goings-ons with Renquist! I have more or less gotten the code in a somewhat usable state, and have removed some more hard-coded elements. In addition, I've worked on adding code so that Python's distutils can package up the software automatically. Now Renquist can be installed as a true library!

Don't expect code anytime soon, however. Since my name is going on the software, there's no chance that I'm going to be letting it out the gate without some serious vetting, and that means unit testing (via Python's unittest). Also, I haven't yet hooked up author and enclosure code.

And finally, there's no documentation, which is just unacceptable for a library. I haven't yet settled on what software to use. pydoctor looks nice, but there are no official releases to download. Epydoc does have releases available for download, though...I guess I'll cross that bridge when I come to it.

There's all of the big news right now. Fun fact: I'm currently building Renquist as version 0.0.0 alpha. When it's releasable, it'll probably be called version 0. Exciting!

Capturing comments

I stopped using coComment for a long while, and that turned out to be a mistake: I stopped commenting on others' content altogether! I'm back on coComment, and I'm loving it. If you visit my user page, you'll see that I'm actively commenting in many places.

One of the biggest problems I have with commenting is that my comment (that is, my copyrighted content) is no longer under my control. coComment gives me the ability to capture my words and store them away safely. However, it doesn't go far enough (and can't). In particular, I want to have a little signature at the bottom of my comments that points back to my own site, to the original comment, together with a GPG signature.

So, for example, I might go to Andy's blog and leave a comment that says Maintain your blog!. Before submitting the comment to his blog, it would receive a unique identifier from my blog, get cryptographically signed, and then get submitted to his blog.

I've also considered that some sites might not like a link at the bottom of the comment; that's why it's unlikely that the little signature at the bottom of the comment would be a direct link to my site. I've also considered making the entire thing a block of text that search engines would treat as a single entity, so that the signature could be searched for easily. Perhaps it would be along the lines of kurtmckee.org-comment-4238.

I do wish that blog systems supported cryptographic signatures in a smart way. OpenPGP and GPG can help establish identity because they intrinsically depend on a web of trust. Building such tools directly into blog systems seems like a good way to help people interact with trust. I could be wrong, but those have been some of my recent thoughts about commenting.

Call me

I finally figured out what the proper settings are for my audio card, so Audacity is now able to record the audio coming from my cell phone, which means that you all need to start calling me and leaving hilarious messages for me to record and store away for the cold nights. Tell me a joke. Drunk dial me. Forget who you're talking to and end the message with Love you! and then burst out in embarrassed giggling and hang up.

That, or do something that I haven't heard before.

Password policies

I've finally gone through and updated almost all of my passwords on the various services that I use. Having crafted devilishly unwieldy passwords for this year, it is infuriating to run across services that won't allow me to use my strong passwords. Digg and coComment are the biggest well-known offenders; Digg forbids any special characters, and coComment arbitrarily accepts certain special characters but not others. I emailed coComment about their policy, and received this in reply:

You may use only [sic] following symbols in addition to letters and digits:
$ @ - # % & ? . - _
It is a matter of security.


Security? Have they not escaped their input properly? Are they afraid of SQL injection attacks? At least they're aware of their limitations. When I changed my SourceForge password their system claimed that the change failed, while it in fact changed the password just fine.

But I can't end this post without giving special props to my Northwestern University IT people.

The Northwestern University password policy is the most onerous I've ever had to comply with. In particular, passwords must be between six and eight characters, and passwords expire every three months, and I've suffered under their foolishness for years. My passwords start at 10 characters!

Anyway, that's 31 services down, and only a few more to go. Hooray!

Cloverfield (2008)

I first heard about Cloverfield over at Empty Bookshelf Reviews. Nate's review of Cloverfield convinced me to see the movie as soon as possible (and had that not been enough, my friend Dan weighed in on Cloverfield soon after). So here's my brief review:

Do not fail to see Cloverfield in theaters.

I'm not going to discuss the plot, and I recommend avoiding any trailers, because the movie was shot from the point of view of one of the characters, who is carrying a camcorder. As such, several plot devices become available. One is that the audience is not given any foreshadowing or special knowledge -- we see what the characters see. You don't get to see shadows on the wall just behind the oblivious protagonists, nor evil creepers approaching while someone sleeps, nor does the camera pan to give you a glimpse of what's on the other side of a wall. Other movies do that; this one doesn't. For that reason, I think it would be appropriate to go into the movie with as little information as possible.

As one of the few movies I've been excited about enough to see in theaters, Cloverfield was just as exciting as I could have hoped, and more. Go see this movie.

Box o' legos



Transcription:

So...I've come into some legos recently...a WHOLE LOT of legos, ALRIGHT! It's like Uncle Scrooge's money bin. Let me just pull back to kind of show you what we're dealing with here. We're kind of dealing with a big boxful of legos.

Now don't think that just because there's a lot of pieces in here that there're also a lot of sets, because that is not the case. As an example, take a look at this guy. I have found a lot of leg pieces in this box. What I have not found is a corresponding number of torsos and heads, which has been just a little bit disappointing. I have, however, found a lot of pieces that are new and exciting to me -- I haven't seen them before -- such as this guy, which I can only describe as some form of fiber optic witchcraftery...

I work at a toy store, and for their grand opening they asked Lego "Would you guys send some freebies so we can give them out to the kids when we open". And Lego said "Oh yeah yeah! I think we've got some freebies..." But they sent this box instead, which doesn't do us any good because it's a bunch of individual pieces that don't sum up to any complete set. So it languished in our basement for a long time and was recently rediscovered, at which point the owners said "Kurt. Would you like some legos?"

And I said "Uh, yeah?"

And that's the true story of how I've come to be in possession of a big boxful of individual legos that don't sum up to any complete set.