Sun, 12 Feb 2006
I've thought for quite a while that I wanted to sort out my various TODO lists into something more general, and integrate it with a calendar of some sort.
My requirements didn't seem to extreme:
* access from any machine or some way to synchronise machines * calendar and TODO list integrated * let me know whats coming up and what I need to do soon
I looked at Chandler version 0.6.0, which is marked as "experimentally usable" but unfortunately I found it to be unusable. I see 0.6.1 is released now, so that might be better, but I've not tried it. But Chandler seemed to be overkill for what I wanted anyway.
So I started looking at command line applications. I experimented with ccal which seems quite nice. It's written in python and I even started hacking on it to make it do some more of what I wanted and some more of what it should have done, but in the end it just didn't do enough of what I wanted. Here's my patch, anyway:
--- /home/pjcj/utils/ccal06.py.org 2004-08-29 18:01:59.000000000 +0200 +++ /home/pjcj/utils/ccal 2006-02-12 17:38:08.230850964 +0100 @@ -182,11 +182,17 @@ print "ccal entries:\n" if cal: print "Calendar:\n" - for item in self._cal.getItems(): - if not str(item.__class__)=="__main__.ccalItem": - item=ccalItem(item) - - print item.entry + for i in range(999) : + viewtime = (datetime.datetime(self._cal.viewtime[0],self._cal.viewtime[1],self._cal.viewtime[2])+datetime.timedelta(days=i)).timetuple() + entries=self._cal.getItems(viewtime) + dateString = time.strftime(self._cal.dateformat,viewtime) + dateString += " ("+self.friendlyDateTimeDelta(datetime.datetime(viewtime[0], viewtime[1], viewtime[2]) - datetime.datetime(self._cal.localtime[0],self._cal.localtime[1],self._cal.localtime[2]))+")" + if entries!=None and len(entries) > 0: + entries.sort(lambda x, y: cmp(ccalItem(x).entry, ccalItem(y).entry)) + for item in entries: + if not str(item.__class__)=="__main__.ccalItem": + item=ccalItem(item) + print dateString+": "+item.entry print "\n" if todo: print "Todo list:\n" @@ -523,6 +529,7 @@ ypos+=1 + entries.sort(lambda x, y: cmp(ccalItem(x).entry, ccalItem(y).entry)) for entry in entries:
Then I started to take a look at some vim plugins hoping for better luck. I found a couple that individually seemed to do part of what I wanted. First there was VimOutliner, which will do nicely for managing my todos. Then there was calendar.vim, which will deal with the calendar part. Now all I needed to do was join them up so that todos with a date went into the calendar. I little bit of Perl sorted that out. So now I have the infrastructure. All I have to do now is use it.
#!/usr/bin/perl # Copyright 2006, Paul Johnson (http://www.pjcj.net) use warnings; use strict; use File::Find; my $dir = <~/g/calendar>; chdir $dir or die "Can't chdir $dir: $!"; my %todos; my $outline = "pjcj.otl"; # Read the outline file and note any entries that have associateed dates. open my $f, "<", $outline or die "Can't open $outline: $!"; while (<$f>) { # Dates have the format YYYY-MM-DD push @{$todos{$2}{$1 eq "X" ? "done" : "open"}}, $3 if /^\s*(?:\[(.)\])?\s*(?:\d*%)?\s*(20\d\d-[01]\d-[0-3]\d)\s*(.+)/; } my ($y, $m, $d) = (localtime)[5, 4, 3]; my $today = sprintf "%04d-%02d-%02d", $y + 1900, $m + 1, $d; print "Today is $today\n"; sub write_calendar { my ($cal) = @_; # Get the date from the filename. my ($y, $m, $d) = $cal =~ /\d+/g; my $date = sprintf "%04d-%02d-%02d", $y, $m, $d; my %entries; if (-e $cal) { # Read in the calendar file, ignoring any existing todos. open my $f, "<", $cal or die "Can't open $cal: $!"; my @l = grep /\S/ && !/^(?:TODO|DONE) /, <$f>; chomp @l; @entries{@l} = (); # Delete the file - there may be nothing more to write to it. unlink $cal or die "Can't delete $cal: $!"; } # Add the todos from the outline. @entries{map "TODO $_", @{$todos{$date}{open}}} = (); @entries{map "DONE $_", @{$todos{$date}{done}}} = (); { # If there's nothing to be done, just get out. last unless %entries; # Write the new calendar file. open my $f, ">", $cal or die "Can't open $cal: $!"; print $f "$_\n" for sort keys %entries; # Print out what's coming up. # Don't print if it's in the past and there are no todos, # or if it's just todos and they are all done. last if $date lt $today && !@{$todos{$date}{open}}; last if keys %entries eq @{$todos{$date}{done}}; print "$date\n"; print " $_\n" for sort keys %entries; } # We're finished with this date. delete $todos{$date}; } sub wanted { # We're only interested in calendar files. return unless -f; unlink, return if -z; return unless /\.cal$/; write_calendar $_; } { no warnings "numeric"; # Go through the calendar files in chronological order. find({ wanted => \&wanted, preprocess => sub { sort { $a <=> $b } @_ }, no_chdir => 1 }, <20??>); } # Now take the remaining todos and turn them to the calendar files. for my $date (sort keys %todos) { # Locate the calendar file from the date. (my $cal = $date) =~ s|-0?|/|g; $cal .= ".cal"; write_calendar $cal; }