During my discovery of the neat list of todos which you can print from iCal I also discovered that a whole bunch of my todos didn’t had a due date. Unfortunately ICal prints todos without a due date even if they are completed.
Well, I don’t care about the due date if the todo is completed, so the idea was born to set the due date to the value of the completion date with a small AppleScript. This should be easy, just find every todo with a completion date and a due date with missing value. Then set the due date to the completion date - job done.
Because I have a lot of todos I didn’t want to iterate over each and every todo. There should be a possibility to let iCal do the work of filtering the todos.
To start with something I tried to find all todos without a due date:
tell application "iCal"
get todos of calendars where due date is missing value
end tell
Result: missing value can’t be converted into a date
Ok, it seems that some more investigation is necessary here. The missing value is somewhat like NULL in SQL, there is no value. The keyword “is” is not like the SQL keyword, it is just a synonym for equals. That means AppleScript has to convert missing value to a date in order to compare both values. It turns out that this is impossible for AppleScript. (That is the same in SQL if you try something = NULL…)
But missing value shares something else with the SQL NULL: If you compare missing value with anything else you get false as the result. (Well not exactly. If you compare missing value with missing value that will give true. So the behaviour is not exactly the same as in SQL.)
Now this piece of knowledge is enough to write a filter which will work. All we need is a date to compare with and which is guaranteed outside our due dates. I picked a date far enough in the past to be sure I had no duties at that time.
tell application "iCal"
-- at least I had no todos before that date...
set d to date ("01.01.1000")
-- find the todos which are not completed
-- due date = missing value
set todosToComplete to (todos of Calendar 1 where not (completion date > d))
-- for these todos set the due date to completion date
repeat with theToDo in todosToComplete
copy {ident:uid, prio:priority, descr:summary} of theToDo to the end of myTodos
end repeat
myTodos
end tell
The tricky part is “not (completion date > d)”. If you just say “completion date > d” you will get all todos completed after our arbitrary start date but no todo with a completion date with missing value. To get every todo with a completion date the date we compare to has simply to be far enough in the past. Now we get all completed todos. To get the ones which are not completed we simply negate the condition with “not”.
Ok, ready for the solution of my task to set all due dates to the completion dates if the due date is missing and the completion date is set.
tell application "iCal"
-- at least I had no todos before that date...
set d to date ("01.01.1000")
-- for each calendar
repeat with theCal in calendars
-- find the todos which are completed but have no due date
-- due date = missing value and completion date <> missing value
set todosWithMissingDueDate to (todos of theCal where not (due date > d) and (completion date > d))
-- for these todos set the due date to completion date
repeat with theToDo in todosWithMissingDueDate
tell theToDo to set due date to completion date
end repeat
end repeat
end tell
No need for iterating over every todo anymore!
Happy Scripting!
Model: MacBook Pro
AppleScript: AppleScript 2.1.2
Browser: Safari 534.55.3
Operating System: Mac OS X (10.6)