GTD and the iPod

I’d never paid much attention to the Notes feature on my iPod, but shortly after I wrote my last two posts (this one and this one), I learned that you can simply copy a text file into the Notes folder of the iPod’s disk image. While I think this is best used for keeping a portable version of your important lists, I couldn’t resist the temptation to write a quick script to take my plain text GTD files and put them on my iPod.

As I said in my earlier posts, I keep my “next actions” and “waiting for” lists in a set of text files in a folder named “gtd” in my home directory. The different files are named according to context--work, home, computer, etc.--and are sectioned according to project. The details are here (after the jump). What I wanted, then, was a script that would make a folder on the iPod for each context and a file for each project within that context. All these folders would go into a “GTD” folder within the iPod’s “Notes” folder.

First, I opened the disk image of my iPod in the Finder and made a GTD folder in the pre-existing Notes folder. Then I worked out this Perl script:


use File::Basename;
use File::Path;

$usage = <<USAGE;
gtd2ipod: split all the .txt files in the gtd folder
          into projects and save them as notes in my
          iPod (which must be connected).

my $inpath  = "~/gtd";
my $outpath = "/Volumes/myipod/Notes/GTD";

# Clean out the GTD folder on the iPod.
my @oldfolders = glob("$outpath/*");
rmtree \@oldfolders;

# Get the list of gtd context files.
@files = glob("$inpath/*.txt");

# Go through each context file, creating a subfolder in the iPod's
# Notes folder and making a file for each project.
foreach $fname (@files) {
  my ($context, undef, undef) = fileparse($fname, qr/\.[^.]*/);

  # Slurp in the whole file.
  open GTD, "<", $fname;
  my $full;
    local $/;
    $full = <GTD>;

  # Create a hash with project names as keys.
  @pieces = split /\s*^([^\n]+)\n-+\n+/ms, $full; # (blank, name, text, etc)
  shift @pieces;                                  # (name, text, etc )
  %projects = @pieces;                            # (name=>text, etc )

  # Make a folder for the context
  my $notepath = $outpath . "/" . $context;
  mkdir $notepath unless (-d $notepath);

  # Write a file for each project.
  foreach (sort keys %projects) {
    my $projfile = $notepath . "/" . $_;
    open PROJECT, ">", $projfile or
      die "Can't open file $projfile for writing: $!";
    print PROJECT $projects{$_};
    close PROJECT;

The most unusual thing about this script, I think, is the use of capturing parentheses in the split command. This puts the captured portion of the separator text into the resulting list, intermixed with the text between the separators. I don’t think I’d ever done that with a split before, but it was very handy in this case, as it kept the project names in @pieces list. I also like the next two lines of the script, where the initial item--always an empty string because my GTD files start with a project name--is removed from the @pieces list and the list--which is now in the form (_project-name-1_, project-text-1, project-name-2, project-text-2, etc.)--is converted to a hash. Perl is just so good at letting you do things like that. I had never converted a list to a hash before, and didn’t remember seeing it done in the Perl books I have, but I just knew that %projects = @pieces would work. Thank you, Larry.

I called this script gtd2ipod and put it in my ~/bin directory, which is in my $PATH. So far, I’ve run it from the Terminal only, but I’m thinking about installing [Do Something When][6] and letting it run the script automatically when the iPod disk image is mounted (or just before it’s unmounted--don’t know about DSW to know if it will handle that case).

Now I can open the Notes on my iPod, and click on the GTD item to see the list of all my contexts. After clicking on a context, I see all the projects for that context. By default, the Notes are under the Extras item of the main menu. I’ve changed the settings on my iPod to put the Notes item in the main menu, which saves me one step of digging.