# todosfromfile.py # By Matthew Dixon Cowles, matt@mondoinfo.com # # Provided with no warranty. # Distributable under the GNU GPL. # Requires Python 2.7 but only because I used the argparse module. # The appscript module is at: http://appscript.sourceforge.net/ # # Version 1.1 # September 6, 2010 # Removed some unnecessary machinery # # Version 1.0 # August 29, 2010 # # Adds to-dos to the program Things by Cultured Code according to # a text file of templates and its command-line arguments. # # Expects its template text file to have two sorts of lines. # "Time-before" lines and to-do item lines. It should look like: # # 1 Month # A to-do # # 2 Weeks # Another to-do # A third # # 4 Days # Another # # On Day # Another # # Blank lines and lines begging with a "#" are ignored. # # Command-line options are: template file name (--file), event name # (--name), and event date (--date, format YYYY-MM-DD). The program # creates a Things project with the name of the event and adds # to-dos to the project with the text from the template file and a # due date that's as far in advance of the date specified on the # command-line as the previous time-before line in the file # specified. import appscript as aps import argparse import datetime def isInt(s): try: dummy=int(s) except ValueError: return False else: return True def getArgs(): parser=argparse.ArgumentParser() parser.add_argument("--file",required=True,action="store") parser.add_argument("--name",required=True,action="store") parser.add_argument("--date",required=True,action="store") args=parser.parse_args() try: date=datetime.datetime.strptime(args.date,"%Y-%m-%d") except ValueError: print "Date must be in format YYYY-MM-DD" raise SystemExit # strptime() gives us unnecessary time values eventDate=datetime.date(date.year,date.month,date.day) return args.file,args.name,eventDate def getTemplates(filename): items=[] timeBefore=None timeBeforeUsed=True for line in file(filename): line=line.rstrip() if line=="" or line.startswith("#"): continue s=line.lower().split() # We don't care about "month" vs "months" if len(s)==2 and s[1].endswith("s"): s[1]=s[1][:-1] if len(s)==2 and ((isInt(s[0]) and s[1] in ("day","week","month")) \ or (s[0]=="on" and s[1]=="day")): # We have a "time before" line if not timeBeforeUsed: print line,"Comes after another time-before line that wasn't used" raise SystemExit timeBeforeUsed=False if s[0]=="on" and s[1]=="day": timeBefore={"days": 0, "weeks": 0, "months": 0} elif s[1]=="month": timeBefore={"days": 0, "weeks": 0, "months": int(s[0])} elif s[1]=="week": timeBefore={"days": 0, "weeks": int(s[0]), "months": 0} elif s[1]=="day": timeBefore={"days": int(s[0]), "weeks": 0, "months": 0} else: assert 1==0,"Can't happen" else: # We have a to-do if timeBefore==None: print "To-do before any time-before lines" raise SystemExit timeBeforeUsed=True items.append({"text": line, "time before": timeBefore}) return items def subtractDate(date,timeBefore): if timeBefore["days"]==0 and timeBefore["weeks"]==0 and timeBefore["months"]==0: return date if timeBefore["days"]!=0: return date-datetime.timedelta(days=timeBefore["days"]) elif timeBefore["weeks"]!=0: return date-datetime.timedelta(weeks=timeBefore["weeks"]) elif timeBefore["months"]!=0: y=date.year m=date.month-timeBefore["months"] if m<=0: m+=12 y-=1 # What's October 31 minus 1 month? For our purposes here, it's September 30. daysMinus=0 while True: try: earlier=datetime.date(year=y,month=m,day=date.day-daysMinus) except ValueError: daysMinus+=1 assert daysMinus<=3 else: return earlier else: assert 1==0, "Can't happen" def main(): templateFile,eventTitle,eventDate=getArgs() templates=getTemplates(templateFile) thingsApp=aps.app("Things") proj=thingsApp.make(new=aps.k.project,with_properties={aps.k.name: eventTitle,aps.k.due_date: eventDate}) for template in templates: dueDate=subtractDate(eventDate,template["time before"]) print template["text"],dueDate todo=proj.make(new=aps.k.to_do,with_properties={aps.k.name: template["text"],aps.k.due_date:dueDate}) return None if __name__=="__main__": main()