David Grant's blog

Drupal Audio Handling is a Mess

The state of Drupal audio files support in Drupal is a mess right now. The audio module, which is great by the way, has not been fully updated to 6.x. This effort has probably been hampered somewhat by the fact that "audio nodes" is not the way that most drupalers want to treat audio going forward. The new drupal way is CCK module and Views and the new way to do audio in drupal is by creating a CCK type with a File field. The FileField module is mostly ready for 6.x (but still "alpha"), but one other nice addition, XSPF playlist module, which I need, is not even close. I recently tried to update a site I maintain, willmusic.ca, over to Drupal 6.x. Trying to get an album type and then generating an XSPF feed from the associated file fields was next to impossible without actually porting the XSPF module to Drupal 6.x myself. When I tried to use the audio module in 6.x to get an XSPF feed for an album type with attached audio files (using audio_attach), this also failed, because the audio_feeds module has not been ported to 6.x yet. So in the end, I reverted back to the original 5.x site, and got XSPF feeds working for each album and then hooked up a flash media player that accepts an XSPF playlist. All is good except I wish I was using 6.x. The moral of the story is that even though 6.0 was released over a year ago, 6.x is no where near production ready, unless you have time to develop your own modules, or you don't plan on using any modules at all.

Update (2010-02-03): the SWF Tools module looks promising, especially Flow Player which it integrates with. I starting to port the site over to Drupal 6 and I think CCK is the way to go, so I will go with CCK FileField with SWF Tools.

Tags:

Don't Rewrite Working Black-box Code

At work I am modifying an existing tool to work from the command line instead of a GUI. Currently everything is a bit coupled to the GUI. On Friday, the next problem I encounted was a global variable in Common.py that was not initialized.

""" Common.py """
def initHSCM():
    global hSCManager
    ...
    hSCManager = win32serviceOpenSCManager(None, None, win32con.SERVICE_ALL_ACCESS)
    ...
 
...
def startService(service):
    """ function that uses hSCManager """
    # These functions don't work when hSCManager is set to None
 
def stopService(service):
    """ function that uses hSCManager """
    # These functions don't work when hSCManager is set to None
...

The only place it is getting initialized is when iniHSCM() is called from GUI.py.

""" GUI.py """
Common.initHSCM()
...
startService("blah")
...
stopService("blah")

My new CLI.py does not uses Common.py as well as the other underlying libraries. Instead of just calling initHSCM() inside CLI.py so that calls to Common.py, I decided to rewrite all the functions that use the hSCManager global variable (ie. startService, stopService, and many others). My plan was to rewrite them to not depend on this global variable, and make them a bit cleaner. Instead they would get an hSCManager handle and close it at the end. So service manager objects would be shorter-lived things. Like most of the code in this project, it's a rat's nest and was written by people who knew C and Java better than they knew python. So part of my motivation for re-writing was to clean things up a bit. After I had re-writen a few of the functions, I realized that I had unknowingly creating new bugs (in the functions I was writing). Even though I was writing some unit tests (which was making the development process take longer on the whole) I knew that the code I was writing was going to have more bugs in it than the code that had essentially been working and stable for years (albeit ugly). The last thing I need is bugs in this code. When bugs happen I want to know that they are most likely in my new code.

In the end, I gave up and this is what I did:

""" CLI.py """
Common.initHSCM() # easy hack
...
startService("blah")
...
stopService("blah")

An even easier way is to move responsibility for initializing hSCManager into Common.py. Something like this?

""" Common.py """
hSCManager = win32serviceOpenSCManager(None, None, win32con.SERVICE_ALL_ACCESS)
 
...
def startService(service):
    """ function that uses hSCManager """
    # These functions don't work when hSCManager is set to None
 
def stopService(service):
    """ function that uses hSCManager """
    # These functions don't work when hSCManager is set to None
...

Hmm, that was easy. Moral of the story: don't re-write code that is several years old and works unless you really, really have to. Who cares if it's ugly and a hack. You didn't write it.

Tags:

Ticket to Ride

[img_assist|nid=333|title=Ticket to Ride: Europe|desc=|link=none|align=left|width=256|height=192]
I recently purchased Ticket to Ride: Europe, an excellent board game by Days of Wonder. I've played it four times with 4 players, and twice with 2 players and I'm still not tired of it. The best part about this game is how simple it is to play and that games don't take very long to play either. I was looking for an alternative for Akham Horror, a game with tons of rules that requires at least 3 hours to play. Ticket to Ride is simple, as there are only a few basic concepts that need to be understood. We even played with my in-laws (who haven't played many board games before) and one of them almost won. The other didn't complete any of their Destination Tickets but they were still able to play and make routes along the way. I think he has figured it out now and if he plays a second time he will know what he is going. He actually ended up breaking some of the longer routes thus preventing my wife and I from getting the extra 10 points for the longest route. The other great thing about this game is that is plays well with 2 players. I highly recommend this game for anyone.

Weight Gain During Pregnancy Progress Tracker Spreadsheet

When woman get pregnant they are supposed to gain weight. Not gaining enough weight can cause problems just as gaining too much weight can cause problems. But when you are 4 months pregnant or when you are 7 months pregnant it is hard to know how much you should weigh at that point in your pregnancy. I made a spreadsheet to help my wife know at any point of her pregnancy whether she is underweight or overweight relative to the end target.

The spreadsheets are available below in Excel and OpenOffice format. I assumed a weight gain of 4-6 lbs in the first trimester followed by steady weight gain all the way until the end of the pregnancy. The defaults are a minimum gain of 25 lbs, a target gain of 27.5 lbs, and a maximum gain of 30 lbs. The minimum and maximum bands are visible on the chart and the goal is to stay within those bands. You only need to enter your starting weight, your date of last menstruation and the low, medium, and high targets on the "Input" sheet and enter your weight weekly (you can skip weeks too and it will still work) on the "Graph" sheet. The defaults are for someone with average BMI.

Supposedly in the U.S., woman with normal BMIs are advised to gain 25-35 lbs whereas in Canada they are advised to gain 25-30 lbs (see "CBC: Pregnancy weight gain guidelines may be too high"). If you have a high BMI you should gain a less weight and if you have a low BMI you should gain a bit more. There are lots of other websites or books with more information on this.

Update: Check out this update from Todd: http://www.epiphanyofthefox.com/private/new_pregnancy_weight_gain.xlsm.

Tags:

Using XML for Code Documentation is Just Plain Wrong

I was just looking at some C# code at work today and it had XML Documentation (like javadoc or python docstrings, only with XML). Who was the idiot that came up with that idea? It's the most insane thing I've ever seen. Let's look at the predecessors to C#'s XML documentation:

Javadoc:

/**
 * Returns an Image object that can then be painted on the screen. 
 * The url argument must specify an absolute {@link URL}. The name
 * argument is a specifier that is relative to the url argument. 
 * <p>
 * This method always returns immediately, whether or not the 
 * image exists. When this applet attempts to draw the image on
 * the screen, the data will be loaded. The graphics primitives 
 * that draw the image will incrementally paint on the screen. 
 *
 * @param  url  an absolute URL giving the base location of the image
 * @param  name the location of the image, relative to the url argument
 * @return      the image at the specified URL
 * @see         Image
 */
 public Image getImage(URL url, String name) {
	try {
	    return getImage(new URL(url, name));
	} catch (MalformedURLException e) {
	    return null;
	}
 }

Then, doxygen, which looks a lot like javadoc:

      /**
       * a normal member taking two arguments and returning an integer value.
       * @param a an integer argument.
       * @param s a constant character pointer.
       * @see Test()
       * @see ~Test()
       * @see testMeToo()
       * @see publicVar()
       * @return The test results
       */
       int testMe(int a,const char *s);

Unfortunately Genshi doesn't syntax highlight the javadoc comments. But it looks fairly readable. Let's try a python docstring example. There is no one standard. One of the documentation generators for Python, Epydoc understands plaintext, javadoc, epydoc, and reStructuredText.

Python code with epydoc style docstrings:

def x_intercept(m, b):
    """
    Return the x intercept of the line M{y=m*x+b}.  The X{x intercept}
    of a line is the point at which it crosses the x axis (M{y=0}).
 
    This function can be used in conjuction with L{z_transform} to
    find an arbitrary function's zeros.
 
    @type  m: number
    @param m: The slope of the line.
    @type  b: number
    @param b: The y intercept of the line.  The X{y intercept} of a
              line is the point at which it crosses the y axis (M{x=0}).
    @rtype:   number
    @return:  the x intercept of the line M{y=m*x+b}.
    """
    return -b/m

Python code with one example of reStructuredText docstrings (this one includes the types of the parameters but they aren't necessary):

def fox_speed(size, weight, age):
    """
    Return the maximum speed for a fox.
 
    :Parameters:
      size
        The size of the fox (in meters)
      weight : float
        The weight of the fox (in stones)
      age : int
        The age of the fox (in years)
    """
    #[...]

I couldn't find any nice examples for C# XML Documentation. The C# XML Documentation Tutorial has some examples, but conveniently, none that include all the tags that I would need to replicate the javadoc example I showed above. So I'll convert the Java example to C#:

   /// <summary>
   /// Returns an Image object that can then be painted on the screen. 
   /// The url argument must specify an absolute {@link URL}. The name
   /// argument is a specifier that is relative to the url argument. 
   /// 
   /// This method always returns immediately, whether or not the 
   /// image exists. When this applet attempts to draw the image on
   /// the screen, the data will be loaded. The graphics primitives 
   /// that draw the image will incrementally paint on the screen.</summary>
   /// 
   /// <param name="url">an absolute URL giving the base location of the image</param>
   /// <param name="name">the location of the image, relative to the url argument</param>
   /// <returns>
   /// the image at the specified URL</returns>
   /// <seealso cref="Image">
   /// Read more about the Image class</seealso>
 */
 public Image getImage(URL url, String name) {
	try {
	    return getImage(new URL(url, name));
	} catch (MalformedURLException e) {
	    return null;
	}
 }

I followed Microsoft's convention (because they know best) of putting the opening tags on a line on their own.

The javadoc sucks because you have to put a <p> (or <br />?) to make a new line which is stupid. Otherwise it's pretty readable, and same goes for doxygen. Especially the @param and @return tags. The Epydoc-style python docstrings suck. You have to specify the type using a @type tag and the return type using an @rtype tag. The reStructuredText example looks the best to me. No tags at all, except for the :Parameters: heading which should be there anyways. The C# comments are an eyesore. Even if Visual Studio had syntax highlighting for the comments it would suck. Did Microsoft look at the two major previous implementations (doxygen and javadoc) and decide that XML was a better way to document code?

I recently saw an interesting comment in scipy's source about one of scipy's guiding principles in designing the docstring standard for their codebase:

A guiding principle is that human readers of the text are given precedence over contorting docstrings so our tools produce nice output. Rather than sacrificing the readability of the docstrings, we have written pre-processors to assist tools like epydoc_ and sphinx_ in their task.

Microsoft clearly took the opposite route and decided to make code documentation readability by human readers a low priority.

Why I Chose Python Over C#

I was recently tasked with writing a sipmle UI and the choice of what language/framework to use quickly boiled down to two choices: C# or Python. I eventually chose Python and here's why:

XML

When I started sketching out a design for this project in C#, I ended up envisioning lots of XML configuration files, in place of compiled-in, hard-coded configuration. In Python, however, XML is rarely required. Instead you can use python modules and data structure to specify your configuration.

You can notice this difference clearly if you look at C# and Java web frameworks and compare them to Python web frameworks. Django (a Python web framework) has a few configuration files. One is settings.py which is basically just a list of key value pairs. Some of the values, however, are Python tuples (immutable lists). It looks like a hybrid between a .ini file and an xml file. The beauty of this is that you can actually run a program like pychecker or pylint on your program and if you are trying to access a key in your settings file that doesn't exist, it will complain! Try doing that in a compiled language. So Django and other programs just take advantage of the fact that since code isn't compiled, you can put all your config in code, and you can easily tweak it later without needed to re-compile anything.

In web frameworks and other projects for C#/.NET and Java, XML is a huge part of configuration. Spring, Hibernate, Ant, log4j/log4net all rely on either XML configuration or annotations (which just couple settings to your code and bake it into the build). So if you write your own applications in Java/C# you will find yourself also using XML for configuration and writing code to parse and possibly write XML as well. The only time I ever enjoyed using XML was when I was using the ElementTree library for Python, which is now a standard library in Python.

Less code

Python code is generally a lot shorter. This means it's quicker to write, quicker to read, quicker to debug, quicker to modify later. No braces, no semi-colons, terse data structures, list comprehensions.

No compiling

Being able to modify code in the field is huge. Many times we've modified Python code in the field using a text editor after getting some obscure error and then re-ran the script. No re-compilation necessary.

Libraries

I tried porting a simple python script to C# just to see how easy/hard it would be. The first step was porting the command-line options parsing. I used GNU getopt style parsing, which is included in python's getopt library. No such thing is included in .NET. There is a third-party library, CSharpOptParse. Having to download this was a bit of a turn off. Then I looked for an example of CSharpOptParse usage and I found one. Ugh. The python getopt example is much nicer. If you don't like getopt there is also optparse (apparently, "optparse is a more convenient, flexible, and powerful library for parsing command-line options than getopt". I'll have to give it a try!). It looks even simpler than getopt!

The next thing I to find was to look at how to call an external executable in C#/.NET and capture stdout and/or stderr. Talk about annoying. Python's new-ish subprocess library is awesome.

Finally my Python script does some path splitting using os.path.split and os.path.splitext. I did find .NET's Path class to be pretty convenient, although no better than python's os.path.

Documentation

The Python documentation is far better than anything I have seen in the .NET/C# world. Maybe it's because smart people use Python.

Conclusion

I've been a long-time Python user but I do like languages like C# and Java as well, but when I put Python and C# side-by-side for this simple little project, nothing competes with it.

Tags:

How to disable ccmexec.exe

ccmexec.exe was using a lot of CPU and I/O. Disabling the "SMS Agent Host" service gets rid of it.

Tags:

How To Stop Chrome Thrashing to Disk

Google Chrome will thrash to disk a lot to disk, at least in version 0.2.149.27, build 1583 (the first publically available beta) in Windows XP. It is so bad that my entire computer locks up a bit and becomes unresponsive. Disabling phishing and malware protection seems to stop the thrashing:

  1. Click on the monkey-wrench icon to the right of the address bar.
  2. Choose "Options" from the menu.
  3. Click the "Under the Hood" tab
  4. Disable the "Enable phishing and malware protection" check-box

[img_assist|nid=321|title=Google Chrome Disable Malware and Phishing Protection|desc=|link=none|align=none|width=418|height=315]

According to procmon it is writing to this filename: <systemdriveletter>:\Documents and Settings\<username>\Local Settings\Application Data\Google\Chrome\User Data\Safe Browsing. There is only one other site where people are talking about this thrashing problem, at Ars Technica.

Tags:

init.d Script for Trac on Ubuntu Linux

I modified an nginx init.d script and created an init.d script for trac.d. I run tracd and then forward traffic from trac.davidgrant.ca to trac's port (using nginx) rather than using cgi or fast-cgi. Please let me know if you have any problems with this scripts and I will fix it.

#! /bin/sh
### BEGIN INIT INFO
# Provides:          tracd
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the tracd web server
# Description:       starts tracd using start-stop-daemon
### END INIT INFO
 
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/bin/tracd
NAME=tracd
DESC=tracd
#DAEMON_OPTS="-s -r -d -p 8080 --basic-auth=*,/etc/tracd/tracusers,trac /trac"
DAEMON_OPTS="-s -d -p 8000 /home/david/trac --pidfile /var/run/$NAME.pid"
 
test -x $DAEMON || exit 0
 
# Include tracd defaults if available
if [ -f /etc/default/tracd ] ; then
        . /etc/default/tracd
fi
 
set -e
 
case "$1" in
  start)
        echo -n "Starting $DESC: "
        start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
                --exec $DAEMON -- $DAEMON_OPTS
        echo "$NAME."
        ;;
  stop)
        echo -n "Stopping $DESC: "
        start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid
        echo "$NAME."
        ;;
  restart|force-reload)
        echo -n "Restarting $DESC: "
        start-stop-daemon --stop --quiet --pidfile \
                /var/run/$NAME.pid
        sleep 1
        start-stop-daemon --start --quiet --pidfile \
                /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS
        echo "$NAME."
        ;;
  *)
        N=/etc/init.d/$NAME
        echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
        exit 1
        ;;
esac
 
exit 0

After you create the script at /etc/init.d/tracd, don't forget to do the following:

sudo chmod +x /etc/init.d/tracd
sudo /usr/sbin/update-rc.d -f tracd defaults

to make the script executable and to make it run on startup.

Tags:

Not Happy With TortoiseSVN

I don't use svn in Windows much but when I do I usually use TortoiseSVN. I haven't been very happy with it lately. With TortoiseSVN installed, Windows Explorer take 5-10 seconds to start and during that time I can't use the task bar because it's a part of explorer.exe. While I like the Explorer integration idea, I'm not sure if I like how everything in Tortoise involves at least a right-click on a folder. Also, sometimes it's nice to see all your working copies in one window, although the working copies may be spread out all over your hard drive.

So I have been giving RapidSVN a try and so far I like it. Not much to say about really, it looks like a pretty standard VCS client. Only downside is that it doesn't support subversion 1.5 yet and I was planning on upgrading my svn server pretty soon.

Tags:

Cuil Sucks Big Time

I got all excited to try this new Cuil search engine today but was extremely disapointed. The only thing Cuil does well is sucking. The first thing I did was to search for my name to see where on the front page it would show up (on Google I am the first hit). I was a bit surprised when I did not see my home page on the first page, second page, or third page. Maybe my site is not indexed at all. So I decided to search for one of my most popular posts, my Latex Resume Template post. Not only did it not come up but this great resource on LaTeX resume templates was also missing.

Let's look a little deeper and see how shitty Cuil really is. Try searching for "latex resume template" (obviously, remove quotes). The same search for latex resume template on google is orders of magnitude better. It doesn't take very long at all to notice the differences...

Let's look at the hits that Cuil gave us:

Now let's compare with Google's first 10 hits:

There you have it. The funny thing is that Cuil returned slightly more hits than Google (67,900 results for Cuil vs. 63,000 for Google) but it doesn't make a difference when the top 10 are garbage in one and exactly-what-you-are-looking-for in the other. Google must be laughing their asses off right now. Apparently Dvorak thinks Cuil sucks too and he's pretty blunt about it.

Tags:

Thumbs up to Tomato Firmware, Thumbs Down to X-Wrt

I have an old Linksys WRT-54G v3. I have ran various different firmwares over the years, but performance was never a big deal. I was mainly looking for something that allowed to me to access all of the devices features and a nicer frontend than the one that Linksys provides. At one point I was using different firmwares for QOS when we were sharing our connection with some other people in our building. Anyways, I had been using X-Wrt (white russian) for quite some time. It is basically a nice user-friendly version of OpenWrt.

Recently we purchased a Squeezebox Duet and even more recently we purchased a second Squeezebox Receiver. We finally have wirelessly-streamed music in pretty much every room of the house. We had been noticing gaps in the audio and I decided it was finally time to fix it. I discovered a diagnostics page (called "Server & Network Health", available via SqueezeCenter's Help page) and noticed that streaming rates I was getting were much lower than the 3000 kbps that I was expecting. In fact I was actually getting much lower than 1000 kps and had to change the scale of the measurement just to get a proper reading.

I decided to upgrade to Tomato firmware, which I had heard a lot of good things about. Immediately, I was getting about 3000+ kbps and the audio problems were gone. As an added bonus, we are now able to watch MythTV from a wirelessly-connected laptop, which were not able to do before (previously I had assumed that wireless streaming video was just too much for today's wireless networks to handle, or too much for MythTV to handle). I think I am going to make a donation to Tomato because it has made such a big difference for us. We can now enjoy listening to our mp3 library anywhere in the house (without gaps) and can watch recorded TV shows on our laptop.

Make Shared Folders Work in TinyXP as a Guest in VirtualBox

I gave TinyXP Rev 09 a try recently as a guest in VirtualBox. (I do have a legal license for Windows XP, so while that definitely doesn't make downloading TinyXP legal, I feel that morally speaking it's ok).

I wasn't able to browse for shared folders at \\vboxsvr as I expected. It is, however, possible to map these folders to drives using the "net use" command:

net use z: \\vboxsvr\david

That worked perfectly. Have fun!

VirtualBox USB with Windows XP guest in Ubuntu Hardy or Intrepid

[img_assist|nid=366|title=|desc=|link=none|align=right|width=145|height=150]
First of all, USB will not work in VirtualBox if you are using the Open Source Edition (OSE) of VirtualBox. Get the full closed-source edition of VirtualBox from their website. There are many differences between the open-source edition and the closed-source version and one of them is USB.

I finally got USB working with Windows XP running as a guest inside VirtualBox running on a Ubuntu Hardy host. First find out what the group id of the vboxusers group is:

$ grep vbox /etc/group
vboxusers:x:<gid>:david

Then, enable the deprecated /proc/bus/usb inteface:

$ sudo gedit /etc/init.d/mountkernfs.sh

Add the following line after /proc is mounted, at the end of the do_start() function.

#for hardy:
domount usbfs usbdevfs /proc/bus/usb -onoexec,nosuid,nodev,devgid=<gid>,devmode=664
#for intrepid:
domount usbfs "" /proc/bus/usb usbdevfs -onoexec,nosuid,nodev,devgid=<gid>,devmode=664

replacing <gid> with the <gid> you found in the first step. Make sure to reboot. This didn't work for me until I rebooted. There you have it. You shouldn't have to edit /etc/init.d/mountdevsubfs.sh or /etc/fstab as some other internet sources have suggested.

rsync --delete is dangerous!

I hosed my entire home directory on this server last night with an incorrect rsync command. I tried syncing my svn repository to the server like this:

rsync -az -e ssh --delete /var/svn/repos/ servername:/home/username/

The problem is that I had an extra slash on the end of "/var/svn/repos/". Without the extra slash, it would create a directory called repos inside servername:/home/username and sync it with /var/svn/repos. With the slash, it syncs the contents of "/var/svn/repos" with the contents of servername:/home/username, thus causing all files in servername:/home/username to be deleted because of the --deleted command.

What I could have done instead is create a directory on the remote server called repos and then rsync like this (actually you don't need to create repos first):

rsync -az -e ssh --delete /var/svn/repos/ servername:/home/username/repos

Or I could have done this:

rsync -az -e ssh --delete /var/svn/repos servername:/home/username

The last command will create a directory called repos in servername:/home/username and it will sync it but not delete anything in servername:/home/username. Add a trailing slah to /var/svn/repos and it will!

Luckily I had a nightly backup of my entire home directory on my local server's external backup drive. I was worried because although I make these nightly backups I haven't actually inspected them in a while. Fortunately everything was intact.

Tags:

Pages

Subscribe to RSS - David Grant's blog