Google Code + Mercurial = Many Happies

Last night I noticed that Google Code is actually offering the Mercurial project hosting that they promised back in April. I guess it's been around for most of May, but I never saw any news to suggest that it was actually public. As soon as I noticed it, I converted one of my less-known, less-used SVN projects to Mercurial. I'm really liking it.

I need to do a bit more work on this particular project before I announce it to the world, but it's out there, and it's Mercurial powered now babay. I think I will be leaving most of my other projects in SVN so I don't upset all of the other people who actually use them.

Oh, I also noticed that the project quotas were bumped up quite a bit. Now each project seems to get a whopping 1GB of space for free!!! What do you have to say about that, BitBucket/GitHub/Assembla/[insert dirty, rotten free open source project hosting host name here]?!

Hooray for Google Code!

My Fedora 11 Adventures: Part I

Today I decided that I would deliberately put myself outside of my comfort zone. No, not by intentionally putting myself on a telephone for more than 5 minutes this month... I will need a lot more preparation before I can attempt that one. No no, today's experiment has to do with Linux. If you're new around here, I am a very big fan of Linux. It has been my primary operating system for over 8 years (but I still use Windows and Mac occasionally, when I need to test my programs and the cross-platform behavior).

A Little Background On Yours Truly

There was a time when I was what you would call a distro-hopper. I would download any and every Linux distribution I could get my hands on. Most of them would hang around on my computer for a few days at best, but a select few actually impressed me enough to have them stick around for longer. Among those few are Slackware and Sidux. Many other distros are nice and pretty, but when it comes to me being productive on them, there always seems to be something lacking.

I am addicted to speed and reliability--two things that originally urged me to tinker with Linux all those years ago. I am more than willing to sacrifice looks and features for being able to just get something done quickly and efficiently. As a matter of fact, I'm writing this article in VIM, one of the most "light-weight" editors around these days. It allows me to do exactly what I want to do without getting in my way. That's how I like things.

That's probably the main reason I love Slackware. It won't do anything I don't tell it to do. No crazy background processes updating some package repository, slowing down my system. No pestering me about security updates that I will install in my own due time. Slackware only does what I want it to, and I have learned a ton about Linux because of it. If I decide I want something automated in the background, I have to tell the computer to do it. If one of my programs has been updated on the Internet, I download and install the package manually instead of using a "package manager." If one of my programs doesn't work because of a missing dependency, I am the one who finds and downloads the dependency. It's a lot of work initially, but I'm of the persuasion that this work is well worth it for my situation.

In today's day and age, that sort of setup seems to scare a lot of people off. People like to have things "just work." People like to not have to worry about keeping up to speed with what security threats are out there. People like having things to keep them entertained instead of getting things done. People like to see their desktop turn into a cube and spin around. People like to see things glow and wiggle on their computer. It's aesthetically pleasing. There's nothing wrong with that. Unless you want to get things done instead of just stare at your computer.

The Challenge

With that background in mind, you should be equipped to better understand the information and articles that follow. My challenge to myself is this: install Fedora 11 and use it for at least a week. To add to the the challenge, I'm installing the 64-bit version. In my past experience with 64-bit operating systems, there has been no real motivation or necessity for 64-bit computing. It just means more compatibility problems, which reduces productivity. This will be the first 64-bit operating system I actually plan to keep around beyond the exploratory period.

There are a few things about this that will bring me waaaay out of my comfort zone. They are (in no particular order):

  • Fedora
  • RPMs
  • KDE 4

I have a strong disregard for each of these items. There was a time when I considered Fedora to be a respectable platform--back when it was Fedora Core 2 or 3. Ever since then, I feel that it has gone down the tubes. RPMs have always seemed grossly lacking in the speed department to me, and it only got worse after I found out about Debian and Slackware. Finally, KDE 4 seems like one of the absolute worst window managers I have yet to encounter. I love KDE 3.5.x. I wish I could use it everywhere I go. But KDE 4 has yet to appeal to my desire for efficient productivity--it gets in my way almost as much as GNOME does.

Starting today, I plan to look all of these opinions (as biased as they may be) straight in the eye and take 'em head-on. I am going to work on learning to enjoy using Fedora. I'm going to work on learning how to appreciate RPMs. I am going to learn to be productive in the window manager "of the future."

And I will keep you all apprised of my progress.

Checking In

I suppose I should update everyone out there about what I've been up to lately. It seems strange to me that I post article much less frequently now than I did when I was a full-time university student. You'd think I'd have a whole lot more time to blog about whatever I've been working on. I suppose I do indeed have that time, it's just that I usually like to wait until my projects are "ready" for the public before I write about them.

The biggest reason I haven't posted much of anything lately is a small Twitter client I've been working on. Its purpose is to be a simple, out-of-the-way Twitter client that works equally well on Windows, Linux, and OSX. The application is written in Python and wxPython, and it has been coming along quite well. It works great in Linux (in GNOME and KDE at least), but Windows and OSX have issues with windows stealing focus when I don't want them to. I'm still trying to figure it out--any advice would be greatly appreciated.

Chirpy currently does nothing more than check your Twitter accounts for updates periodically. It notifies you of new updates using blinking buttons (which can be configured to not blink). I think the interface is pretty nice and easy to use, but I am its developer so it's only proper that I think that way.

Anyway, that project has been sucking up a lot of my free time. It's been frustrating as I build it in Linux only to find that Windows and OSX both act stupidly when I go to test it. That frustration inspired me to tinker with a different approach to a Twitter client. I began fooling around with it last night, and I think the idea has turned out to be more useful than Chripy is after a month of development!

I'm calling this new project "Tim", which is short for "Twitter IM". This one also periodically checks your Twitter account(s) for updates (of course). However, Tim will send any Twitter updates to any Jabber-enabled instant messenger client that you are signed into. If you're like me, you have Google Talk open most of the day, so you can just have Twitter updates go straight there! You can also post updates to Twitter using your Jabber instant messenger when Tim is running by simply sending a message back!!

The really neat stuff comes in when you start to consider the commands that I've added to Tim tonight. I've made it possible for you to filter out certain hashtags, follow/unfollow users, and specify from which Twitter account to post updates (when you have multiple accounts enabled). I hate all of those #FollowFriday tweets... they drive me crazy. So all I have to do is type ./filter followfriday and no tweet that contains #FollowFriday will be sent to my Jabber client. I love it.

More commands are on the way. Also on the way is a friendly interface for configuring Tim. Getting it up and running the first time is... a little less than pleasant :) Once you have it configured it seems to work pretty well though.

If you're interested in trying it out, just head on over to the project's page (http://bitbucket.org/codekoala/twitter-im/). Windows users can download an installer from the Downloads tab. I plan on putting up a DMG a little later tonight for OSX users. Linux users can download the .tar.gz file and install the normal Python way :) Enjoy!

Update: The DMG for OSX is a little bigger than I thought it would be, so I won't be hosting it on bitbucket. Instead, you can download it from my server.

Don't forget to read the README !!!

Pony Power + Django Critter = Sheer Genius

I just love the Django community. So many good times. I hope that the rest of you find some form of entertainment in the next part of this article.

This morning I woke up to a humorous post by Eric Walstad on the django-users mailing group. It discussed a story about his 9-year-old daughter who has seen the light with Django. She apparently fully understands what Django is capable of and how amazing it truly is. Here is her version of what Django, embodied as a "critter," can do:

Django is a computer programming critter. He is loyal only to computer programmers and does all their work. He types with the ball on the end of his tail, at the speed of light. He beeps when his work is done and when you take him home, he flies around the house, doing all your chores. He's a helpful little fellow.

That just about sums it all up! Django rocks. We already have Pony Power to get us through the day, but when you put Pony Power and the Django Critter together, this is what you get:

Django Pony + Django Critter

Oh man!! Can you feel it? I sure can. Django is amazing, and anyone who's not using it is missing out.

(disclaimer: the characters in the image above remain the property of their respective owners)

Send E-mails When You Get @replies On Twitter

I just had a buddy of mine ask me to write a script that would send an e-mail to you whenever you get an "@reply" on Twitter. I've recently been doing some work on a Twitter application, so I feel relatively comfortable with the python-twitter project to access Twitter. It didn't take very long to come up with this script, and it appears to work fine for us (using a cronjob to run the script periodically).

I thought others on the Internets might enjoy the script as well, so here it is!

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
A simple script to check your Twitter account for @replies and send you an email
if it finds any new ones since the last time it checked.  It was developed using
python-twitter 0.5 and Python 2.5.  It has been tested on Linux only, but it
should work fine on other platforms as well.  This script is intended to be
executed by a cron manager or scheduled task manager.

Copyright (c) 2009, Josh VanderLinden
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
- Neither the name of the organization nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""

import twitter
import ConfigParser
import os
import sys
from datetime import datetime
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.Utils import formatdate

# get the user's "home" directory
DIRNAME = os.path.expanduser('~')
CONFIG = os.path.join(DIRNAME, '.twitter_email_replies.conf')
FORMAT = '%a %b %d %H:%M:%S +0000 %Y'
REPLY_TEMPLATE = """%(author)s said: %(text)s
Posted on %(created_at)s
Go to http://twitter.com/home?status=@%(screen_name)s%%20&in_reply_to_status_id=%(id)s&in_reply_to=%(screen_name)s to post a reply
"""

# sections
AUTH = 'credentials'
EXEC = 'exec_info'
EMAIL = 'email_info'

# make the code a bit "cleaner"
O = lambda s: sys.stdout.write(s + '\n')
E = lambda s: sys.stderr.write(s + '\n')
str2dt = lambda s: datetime.strptime(s, FORMAT)

def get_dict(status):
    my_dict = status.AsDict()
    my_dict['screen_name'] = my_dict['user']['screen_name']
    my_dict['author'] = my_dict['user']['name']
    return my_dict

def main():
    O('Reading configuration from %s' % CONFIG)
    parser = ConfigParser.SafeConfigParser()
    config = parser.read(CONFIG)

    # make sure we have the proper sections
    if not parser.has_section(AUTH): parser.add_section(AUTH)
    if not parser.has_section(EMAIL): parser.add_section(EMAIL)
    if not parser.has_section(EXEC): parser.add_section(EXEC)

    try:
        # get some useful settings from the configuration file
        username = parser.get(AUTH, 'username')
        password = parser.get(AUTH, 'password')

        to_address = parser.get(EMAIL, 'to_address')
        from_address = parser.get(EMAIL, 'from_address')
        smtp_server = parser.get(EMAIL, 'smtp_server')
        smtp_user = parser.get(EMAIL, 'smtp_user')
        smtp_pass = parser.get(EMAIL, 'smtp_pass')

        if '' in [username, password, to_address, from_address, smtp_server]:
            raise Exception('Not configured')
    except Exception:
        E('Please configure your credentials and e-mail information in %s!' % CONFIG)

        # create some placeholders in the configuration file to make it easier
        sections = {
            AUTH: ('username', 'password'),
            EMAIL: ('to_address', 'from_address', 'smtp_server', 'smtp_user', 'smtp_pass')
        }

        for section in sections.keys():
            for opt in sections[section]:
                if not parser.has_option(section, opt):
                    parser.set(section, opt, '')
    else:
        # determine the last time we checked for replies
        try:
            last_check = str2dt(parser.get(EXEC, 'last_run'))
        except ConfigParser.NoOptionError:
            last_check = datetime.utcnow()
        last_check_str = last_check.strftime(FORMAT)

        info = 'Fetching updates for %s since %s' % (username,
                                                       last_check_str)
        O(info)

        # attempt to connect to Twitter
        api = twitter.Api(username=username, password=password)

        # not using the `since` parameter for more backward-compatibility
        timeline = api.GetReplies()
        new_replies = []
        for reply in timeline:
            post_time = str2dt(reply.GetCreatedAt())
            if post_time > last_check:
                new_replies.append(reply)

        count = len(new_replies)
        if count:
            # send out an email for this user
            O('Found %i new replies... sending e-mail to %s' % (count, to_address))
            reply_list = '\n\n'.join([REPLY_TEMPLATE % get_dict(r) for r in new_replies])
            is_are = 'is'
            plural = 'y'
            if count != 1:
                is_are = 'are'
                plural = 'ies'

            params = {
                'is_are': is_are,
                'count': count,
                'replies': plural,
                'username': username,
                'reply_list': reply_list,
                'last_check': last_check_str
            }

            text = """There %(is_are)s %(count)i new @repl%(replies)s for %(username)s on Twitter since %(last_check)s:

%(reply_list)s""" % params

            # compose the e-mail
            msg = MIMEMultipart()
            msg['From'] = from_address
            msg['To'] = to_address
            msg['Date'] = formatdate(localtime=True)
            msg['Subject'] = 'New @Replies for %s' % username
            msg.attach(MIMEText(text))

            # try to send the e-mail message out
            email = smtplib.SMTP(smtp_server)
            if smtp_user and smtp_pass:
                email.login(smtp_user, smtp_pass)
            email.sendmail(from_address,
                           to_address,
                           msg.as_string())
            email.close()

        # save the current time so we know where to pick up next time
        parser.set(EXEC, 'last_run', datetime.utcnow().strftime(FORMAT))

    # write the config
    O('Saving settings...')
    out = open(CONFIG, 'wb')
    parser.write(out)
    out.close()

if __name__ == '__main__':
    main()

Feel free to copy this script and modify it to your desires. Also, please comment if you have issues using it.

Firebug for !Firefox

Pretty much anyone who's been doing any Web development in the last few years probably prefers to use Firefox because of the incredibly powerful extensions it offers. Among the extensions I hear most Web developers complain about not having in other browsers are Web Developer and Firebug. Several people feel that they could get by with another browser (such as Google Chrome) if it only had Firebug.

Well, my friends, the trusty folks who built Firebug actually offer their amazing product for other browsers! It goes by the name of "Firebug Lite." I'm not sure exactly how long this has been around, but the earliest date I can find suggests that it was released to the public in July of 2008.

I happened upon this utility while perusing Django Snippets the other day. A member by the username of jfw posted a middleware which injects the Firebug Lite utility into a response when you're not using Firefox and when your site is in debug mode. I've found it to be quite useful. I hope you all do too!!

New Site Design

I don't know how many of you have noticed this, but I just published a new design for Code Koala. Hooray!!

Along with the change in design came several improvements to the site, including a contact me form and some code optimizations.

Here are some screenshots to illustrate the difference:

Version 1.0

Version 1

Version 2.0

Version 2

jQuery 1.3.0 Released

Today the jQuery team has released version 1.3.0 of their amazing JavaScript toolkit. It boasts a lot of great improvements, such as better selector performance, "live events" (YAY!!), drastically improved delegation filtering, overhauled HTML injection functions, et cetera. I haven't yet had a chance to play with it, but I think it will be quite fun once I do. I have a lot of Django-powered apps that are running on older versions of jQuery.

For anyone who's interested in learning more, check out the jQuery 1.3.0 release notes.

Windows 7 Public Beta Screenshots

Here are some screenshots of the Windows 7 public beta. I installed it in a VirtualBox virtual machine and allocated 600MB of RAM to it.

The new and improved sloading screen

The new and improved sloading screen.

The login screen

The login screen

Logging in....

Logging in....

The desktop.... and a fish!!

The desktop.... and a fish!!

Your choices of Microsoft-sponsored security--the ones that will slow down your computer the most

Your choices of Microsoft-sponsored security--the ones that will slow down your computer the most!

Oh oh!!  It's Internet Explorer 8!  Chews up my site like a charm.

Oh oh!! It's Internet Explorer 8! Chews up my site like a charm.

Captionless taskbar icons until you hover.

Captionless taskbar icons until you hover.

The new Windows Media Player

The new Windows Media Player.

It's version 12!

It's version 12!

Setting your desktop theme

Setting your desktop theme.

Minesweeper wants hardware accelerated graphics

Minesweeper wants hardware accelerated graphics.... why??

All-new Minesweeper

The all-new Minesweeper.

I won!

I won!

Paint

Paint. Very perty.

The start menu

The start menu.

All programs in the start menu

Looking at all programs in the start menu.

Gadgets and the clock thingy

Some desktop gadgets and the clock thingy.

Slashdot

Slashdot in IE 8.

Chrome

Downloading Google Chrome.

WTF?  Verifying application requirements??

What the....? Verifying application requirements??

Ahh... Chrome.

Ahh... Chrome.

Control Panel--stupid people's version

Control Panel--stupid people's version

Control Panel--all options

Control Panel--all options

Administrative tools

Administrative tools

Some things never change... but what's up with the 200MB partition??

Some things never change... but what's up with the 200MB partition??

First UAC popup...

First UAC popup...

Second UAC popup...

Second UAC popup...

Installing Avast

Installing Avast Antivirus.

Windows services

Windows services.... there are a ton of these as usual.

Pong Service for Wireless USB??

Pong Service for Wireless USB??

Heh... Preliminary scan results show that malicious or potentially unwanted software might exist...

Heh... Preliminary scan results show that malicious or potentially unwanted software might exist...

Oh, nevermind... we're good.

Oh, nevermind... Windows says we're good now.

Shutting down... took long enough to get a delayed screenshot.

Shutting down... took long enough to get a delayed screenshot. Could have probably shot 20 more.

Stage 1 of the sloading screen

Stage 1 of the sloading screen.

Stage 2 of the sloading screen

Stage 2 of the sloading screen.

Avast has started its scan

Avast has started its scan.

1% complete... after a few minutes!!

1% complete... after a few minutes!!

Done! after about 30 minutes...

Done! after about 30 minutes...

Third UAC popup...

Third UAC popup...

Trusted publishers?

Trusted publishers? These two screens kept coming up each time I would try to update Avast's antivirus database...

The Resource Monitor.  I was doing nothing at the time.

The Resource Monitor. I was doing nothing at the time.

Activating my copy of Winders 7

Activating my copy of Winders 7

w00t.  I'm legit.

w00t. I'm legit.

Benchmarking my system.

Benchmarking my system.

My VM ranks in at a solid 1.0!

My VM ranks in at a solid 1.0!

Details, details...

Details, details...

The classic Windows theme and a buffalo butt

The classic Windows theme and a buffalo butt...

Why all the games??

Why all the games?? Why not include something a little more productive??

Miscellaneous Site Updates

I figured I should probably post something since I haven't done so yet this year. I've been making several changes to the site lately. Most of them are pretty subtle, but I hope they're useful to you. Updates include

  • You can now send an article to some friends by clicking on the "envelope" icon in the top-right corner of each article. It's a pretty simple mechanism.

  • You can save any of my articles to your hard drive for later consumption in the form of a PDF. Just click the "save" button in the top right corner of any article, and you will be able to view/save the latest revision of the respective article as a PDF. One thing to note on this, though, is that the program I use to generate the PDFs does not support the line numbers in code blocks. Sorry folks.

    For those of you who are interested, I'm using rst2pdf to generate the PDFs from my reStructuredText-based articles.

  • I've removed the calendar from the sidebar and replaced it with my newest useless side project: django-bibliophile. It allows me to share my reading progress with my visitors, because I know you all care that much. I plan on officially releasing the project in the near future.

  • Pagination has been implemented in parts of the article archive.

  • I've added an "article distribution" chart when looking at a year's blog articles.

  • Other random improvements.