I’ve been kicking the tires of TaskPaper lately. I’m intrigued by its minimalist, flexible, plain-text approach to managing a to-do list.
I have a lot of repeating tasks, some with strange intervals. For example, once per year, I download a free copy of my credit report. But I can’t just do it every year on January 1, because if I’m busy one year and don’t do it until the 4th, I have to wait until at least the 4th the following year. You see the problem. The solution is to give myself a buffer, and plan on downloading my credit report every 55 weeks.
Taskpaper has no built-in support for repeating tasks, but its plain-text format makes it easy to manipulate using external scripts. So, for example, I can keep my repeating tasks in an external file, and then once a month have them inserted into my to-do list.
The plain-text calendar tool when, which I also use to remember birthdays, seems like the perfect tool for the job. You store your calendar entries in a text file using a cron-like syntax. You can also do more complicated patterns. For example, I put this line in my file:
!(j%385-116), Transunion credit report
The expression !(j%385-116)
is true whenever the modified Julian day is
equal to 116 modulo 385. This happens every 385 days, starting today.
When I run when
with my new calendar file, I get this output:
today 2014 Feb 22 Transunion credit report
I wrote a quick Python script to translate this into TaskPaper syntax.
1 #!/usr/bin/python
2
3 import argparse
4 from datetime import datetime
5 import re
6 import subprocess
7
8 WHEN = "/usr/local/bin/when"
9
10 def When(start, days, filename):
11 command = [
12 WHEN,
13 "--future={}".format(days),
14 "--past=0",
15 "--calendar={}".format(filename),
16 "--wrap=0",
17 "--noheader",
18 "--now={:%Y %m %d}".format(start),
19 ]
20 return subprocess.check_output(command)
21
22
23 def Translate(line):
24 m = re.match(r"^\S*\s*(\d{4} \w{3} +\d+) (.*)$", line)
25 try:
26 d = datetime.strptime(m.group(1), "%Y %b %d")
27 except AttributeError, ValueError:
28 return line
29 return " - {} @start({:%Y-%m-%d})".format(m.group(2), d)
30
31
32 def NextMonth(date):
33 if date.month < 12:
34 return date.replace(month=(date.month + 1))
35 else:
36 return date.replace(year=(date.year + 1), month=1)
37
38
39 def StartDateAndDays(next_month=False):
40 date = datetime.today().replace(day=1)
41 if next_month:
42 date = NextMonth(date)
43 days = (NextMonth(date) - date).days - 1
44 return date, days
45
46
47 if __name__ == "__main__":
48 parser = argparse.ArgumentParser(
49 description="Print calendar items in taskpaper format")
50 parser.add_argument("filename", help="Name of calendar file")
51 parser.add_argument("-n", "--next", action="store_true",
52 help="Use next month instead of this month")
53 args = parser.parse_args()
54
55 date, days = StartDateAndDays(args.next)
56 out = When(date, days, args.filename)
57 for line in out.split('\n'):
58 if line:
59 print Translate(line)
This takes the when
output, and translates it into something I can dump into
my TaskPaper file:
- Transunion credit report @start(2014-02-22)