Recurring Tasks in iCal

I don’t know if anyone can help me with this, but I’m trying to figure out how to set up recurring tasks in iCal. I’d like to have a recurring event (Pay Rent, or whatever) that spawns a ToDo item 5 days before the event date. I think this could be done by having a 5 day alarm that runs an AppleScript. However, I wish that I could find an AppleScript that would create a ToDo item with the same name as the Event that runs it. Can this be done? How could I accomplish what I’m looking for?

Any help would be REALLY appreciated.

Model: Powerbook
Browser: Safari 412.2
Operating System: Mac OS X (10.4)

Hi Joshua,

This script when run makes a todo for every event in the selcted calendar x days ahead. If you were able to put your reminder events in a seperate calendar perhaps this could work.

set myCalName to "Home" -- change to alarmed calendar
set myAdvance to 5 -- change as needed
set myStart to date (date string of ((current date) + myAdvance * days))
set myEnd to myStart + 1 * days
tell application "iCal"
	set myCal to (first calendar whose title is myCalName)
	set myEvents to (events of myCal whose start date is greater than or equal to myStart and start date is less than or equal to myEnd)
	repeat with myEvent in myEvents
		set theTitle to summary of myEvent
		make todo at end of todos of myCal with properties {summary:theTitle}
	end repeat
end tell

Best wishes

John M

John:

Hey! That’s perfect!

This system works great. I just have a calendar called Tasks with recurring events on the days that each task is due. From that, your script makes recurring tasks in iCal. Very sexy.

Thanks again.
Josh

I updated this code that i found online. It allows you to make an event (event must run alarm of this script), place a note in the notes section that says due …/… This will result in the TODO item due date. It makes a todo item, then deletes the event.
But if you have recurring events that you want to make todo items, it will default to the event date, and the event date will be the due date for the TODO item

(*  This code belongs to Ryan Cuthbertson.  Email igloo@internode.on.net
I would love to hear from you.

Licence:
You may use fragments or ideas from here in your own scripts.  If the size of the fragment used in your script is appreciable (eg. the download subroutine) you must make acknowledgement of its source in your code, and if the script is to be distributed, in your read me/documentation or other appropriate place.  If this sounds too restrictive, contact me for special permission.
If you wish to modify this code and use it for your own purpose, you may, provided you acknowledge this as the original version and do not publicly redistribute your version without permission.  Only the original unmodified version with all its other files (eg. the Read me) can be redistributed.  
Thank you!

Tested for iCal 1.5.5 (Panther) and 2.0.3 (Tiger)
Known probs:
	Don't think it works right on repeating events. Not that it should... Recurring todos, ugh!
	
Version History:
1.0 Initial release
*)

property randomIdentifier : 68894652
-- ^ This is used by the "areYouMe" method which this script will attempt to call on the event's open file alarm file.  The "areYouMe" method will return the random number which will be checked for equality by the main method.
--Else I could make this script an application and use (path to me as text).


--tell application "iCal" to activate
tell application "iCal" to set calList to every calendar
repeat with thisCal in calList
	tell application "iCal" to set convertMePosList to uid of (every event in thisCal whose open file alarms is not {} and start date is less than or equal to (current date) + 20 * minutes)
	-- convertMePosList must be a list of UIDs rather than iCal events because after an event is deleted, the list points to different events.
	repeat with thisEventUID in convertMePosList
		tell application "iCal"
			set thisEvent to item 1 of (every event in thisCal whose uid is thisEventUID)
			set attachedFiles to filepath of every open file alarm in thisEvent
		end tell
		repeat with attachedFile in attachedFiles
			set attachedFile to text 17 thru end of attachedFile
			set attachedFile to my decode_text(attachedFile)
			set areYouMeResult to "" --for now
			try
				set attachedFile to (POSIX file attachedFile) as alias -- may fail if the file has been moved and because iCal is stupid for not updating the path by using the alias system.
				set hopefullyMe to load script attachedFile
				set areYouMeResult to areYouMe() of hopefullyMe
			on error
				--it seems the attached file is not me.  No further action in this iteration.
			end try
			if areYouMeResult is equal to my randomIdentifier then
				--thisEvent is an event requiring conversion.
				tell application "iCal"
					-- Make a new To Do item
					
					set newTodoProps to {}
					
					set todoSummary to summary of thisEvent
					set todoDate to start date of thisEvent
					if todoSummary starts with "start " or todoSummary starts with "begin " then
						set todoSummary to text 7 thru end of todoSummary
					end if
					set newTodoProps to newTodoProps & {summary:todoSummary}
					
					set todoURL to (url of thisEvent) as string
					set newTodoProps to newTodoProps & {url:todoURL}
					
					set newTodoProps to newTodoProps & {description:(description of thisEvent)}
					-- Only works on Tiger  :-(   Don't tell Jonathon.
					
					--get dueDate
					set dueLine to "" --for now
					repeat with thisPara in (every paragraph in (description of thisEvent as text))
						if thisPara starts with "due" then set dueLine to thisPara as text
					end repeat
					if dueLine is not "" then
						try
							--dates start with a number
							if first character of word 2 of dueLine is in "1234567890" then
								set dueDateText to text from word 2 to last word of dueLine
							else
								-- eg. "due on 31/3/06"
								set dueDateText to text from word 3 to last word of dueLine
							end if
							tell me to set dueDate to date dueDateText -- telling iCal doesn't work (?!)
							set newTodoProps to newTodoProps & {due date:dueDate}
						on error
							display dialog "Couldn't work out the due date" buttons {"Cancel", "Set to none"} default button 2 with icon caution
						end try
					else --they didn't put a due date, make the event date the todo date.
						set newTodoProps to newTodoProps & {due date:todoDate}
					end if
					
					make new todo at end of thisCal with properties newTodoProps
					
					set myTodo to properties of the result
					-- log (myTodo)
					
					delete thisEvent
					
					--display dialog "a todo was converted"
					
					exit repeat --we don't need to check the other attachedFiles for thisEvent.
				end tell
			else
				--It's not me that's attached.  Must be some other script with a similar mechanism.  Ignore and continue.
			end if
		end repeat
	end repeat
end repeat


to areYouMe()
	return randomIdentifier
end areYouMe

--Thanks to Apple for supplying the following code (which would not be necessary if iCal was better).
-- this sub-routine is used to decode text strings 
on decode_text(this_text)
	set flag_A to false
	set flag_B to false
	set temp_char to ""
	set the character_list to {}
	repeat with this_char in this_text
		set this_char to the contents of this_char
		if this_char is "%" then
			set flag_A to true
		else if flag_A is true then
			set the temp_char to this_char
			set flag_A to false
			set flag_B to true
		else if flag_B is true then
			set the end of the character_list to my decode_chars(("%" & temp_char & this_char) as string)
			set the temp_char to ""
			set flag_A to false
			set flag_B to false
		else
			set the end of the character_list to this_char
		end if
	end repeat
	return the character_list as string
end decode_text

-- this sub-routine is used to decode a three-character hex string 
on decode_chars(these_chars)
	copy these_chars to {indentifying_char, multiplier_char, remainder_char}
	set the hex_list to "123456789ABCDEF"
	if the multiplier_char is in "ABCDEF" then
		set the multiplier_amt to the offset of the multiplier_char in the hex_list
	else
		set the multiplier_amt to the multiplier_char as integer
	end if
	if the remainder_char is in "ABCDEF" then
		set the remainder_amt to the offset of the remainder_char in the hex_list
	else
		set the remainder_amt to the remainder_char as integer
	end if
	set the ASCII_num to (multiplier_amt * 16) + remainder_amt
	return (ASCII character ASCII_num)
end decode_chars

Hey, folks,

I’m old hat at using Applescripts, but not-so-good at debugging them – my programming savvy lies mostly in mid-nineties Texas Instrument calculator games. Hoo-ee.

I want this script to work for me, and I cant figure out what’s wrong:

set myCalName to "Tasks" -- change to alarmed calendar
set myAdvance to 5 -- change as needed
set myStart to date (date string of ((current date) + myAdvance * days))
set myEnd to myStart + 1 * days
tell application "iCal" 
set myCal to (first calendar whose title is myCalName)
set myEvents to (events of myCal whose start date is greater than or equal to myStart and start date is less than or equal to myEnd)
repeat with myEvent in myEvents
set theTitle to summary of myEvent
make todo at end of todos of myCal with properties {summary:theTitle}
end repeat
end tell

I set up a new calendar, named it tasks, and created a recurring event every day for 14 days from now. I ran the script from within Script Editor, and nothing changed in iCal. I fiddled with the script a little, to no avail. I won’t even show you those changes. Do I need to run from within iCal? As an alarm? Or, is this script outdated in a way I can’t recognize?

Basically, I’m just hoping to find a script that can take a calendar of recurring all-day events (like “Pay Amex” every 15th and “mow lawn” every four days) and turn the ones that are five days away into tasks with due date of the initial event.

This is iCal 3.0.2, Script Editor 2.2, OS X 10.5.2.

Where do I turn?

Thanks, all…

  • John

Hi, faughaballagh. And welcome. :slight_smile:

Since iCal 2.0, calendars have had ‘names’ rather than ‘titles’, so this line:

” needs to be changed to this:

Hi Guys,

I’m trying to edit that script above from jtroy. I’m wanting recurring tasks, with recurring events. However, when I have recurring events, I execute the script, then it creates the to do, but then it deletes the recurring event (ALL of it). I commented out the “delete thisEvent”, but then it would create tasks for previous events within the same calendar. But I am having alot of issues!


(* This code belongs to Ryan Cuthbertson. Email igloo@internode.on.net
I would love to hear from you.

Licence:
You may use fragments or ideas from here in your own scripts. If the size of the fragment used in your script is appreciable (eg. the download subroutine) you must make acknowledgement of its source in your code, and if the script is to be distributed, in your read me/documentation or other appropriate place. If this sounds too restrictive, contact me for special permission.
If you wish to modify this code and use it for your own purpose, you may, provided you acknowledge this as the original version and do not publicly redistribute your version without permission. Only the original unmodified version with all its other files (eg. the Read me) can be redistributed.
Thank you!

Tested for iCal 1.5.5 (Panther) and 2.0.3 (Tiger)
Known probs:
   Don't think it works right on repeating events. Not that it should... Recurring todos, ugh!
   
Version History:
1.0 Initial release
*)

property randomIdentifier : 68894652
-- ^ This is used by the "areYouMe" method which this script will attempt to call on the event's open file alarm file. The "areYouMe" method will return the random number which will be checked for equality by the main method.
--Else I could make this script an application and use (path to me as text).


--tell application "iCal" to activate
tell application "iCal" to set calList to every calendar
repeat with thisCal in calList
	tell application "iCal" to set convertMePosList to uid of (every event in thisCal whose open file alarms is not {} and start date is less than or equal to (current date) + 20 * minutes)
	-- convertMePosList must be a list of UIDs rather than iCal events because after an event is deleted, the list points to different events.
	repeat with thisEventUID in convertMePosList
		tell application "iCal"
			set thisEvent to item 1 of (every event in thisCal whose uid is thisEventUID)
			set attachedFiles to filepath of every open file alarm in thisEvent
			set today to current date
			
		end tell
		repeat with attachedFile in attachedFiles
			set attachedFile to text 17 thru end of attachedFile
			set attachedFile to my decode_text(attachedFile)
			set areYouMeResult to "" --for now
			try
				set attachedFile to (POSIX file attachedFile) as alias -- may fail if the file has been moved and because iCal is stupid for not updating the path by using the alias system.
				set hopefullyMe to load script attachedFile
				set areYouMeResult to areYouMe() of hopefullyMe
			on error
				--it seems the attached file is not me. No further action in this iteration.
			end try
			if areYouMeResult is equal to my randomIdentifier then
				--thisEvent is an event requiring conversion.
				tell application "iCal"
					-- Make a new To Do item
					
					set newTodoProps to {}
					
					set todoSummary to summary of thisEvent
					set todoDate to start date of thisEvent
					if todoSummary starts with "start " or todoSummary starts with "begin " then
						set todoSummary to text 7 thru end of todoSummary
					end if
					set newTodoProps to newTodoProps & {summary:todoSummary}
					
					set todoURL to (url of thisEvent) as string
					set newTodoProps to newTodoProps & {url:todoURL}
					
					set newTodoProps to newTodoProps & {description:(description of thisEvent)}
					-- Only works on Tiger :-( Don't tell Jonathon.

					--get dueDate
					set dueLine to "" --for now
					repeat with thisPara in (every paragraph in (description of thisEvent as text))
						if thisPara starts with "due" then set dueLine to thisPara as text
					end repeat
					if dueLine is not "" then
						try
							--dates start with a number
							if first character of word 2 of dueLine is in "1234567890" then
								set dueDateText to text from word 2 to last word of dueLine
							else
								-- eg. "due on 31/3/06"
								set dueDateText to text from word 3 to last word of dueLine
							end if
							tell me to set dueDate to date dueDateText -- telling iCal doesn't work (?!)
							set newTodoProps to newTodoProps & {due date:dueDate}
						on error
							display dialog "Couldn't work out the due date" buttons {"Cancel", "Set to none"} default button 2 with icon caution
						end try
					else --they didn't put a due date, make the event date the todo date.
						set newTodoProps to newTodoProps & {due date:todoDate}
					end if
					
					make new todo at end of thisCal with properties newTodoProps
					
					set myTodo to properties of the result
					log (myTodo)
					
					--change the current recurring event's starting date to today
					
					set thisEvent to today --previously, there was "delete thisEvent"
					
					--display dialog "a todo was converted"
					
					exit repeat --we don't need to check the other attachedFiles for thisEvent.
				end tell
			else
				--It's not me that's attached. Must be some other script with a similar mechanism. Ignore and continue.
			end if
		end repeat
	end repeat
end repeat


to areYouMe()
	return randomIdentifier
end areYouMe

--Thanks to Apple for supplying the following code (which would not be necessary if iCal was better).
-- this sub-routine is used to decode text strings
on decode_text(this_text)
	set flag_A to false
	set flag_B to false
	set temp_char to ""
	set the character_list to {}
	repeat with this_char in this_text
		set this_char to the contents of this_char
		if this_char is "%" then
			set flag_A to true
		else if flag_A is true then
			set the temp_char to this_char
			set flag_A to false
			set flag_B to true
		else if flag_B is true then
			set the end of the character_list to my decode_chars(("%" & temp_char & this_char) as string)
			set the temp_char to ""
			set flag_A to false
			set flag_B to false
		else
			set the end of the character_list to this_char
		end if
	end repeat
	return the character_list as string
end decode_text

-- this sub-routine is used to decode a three-character hex string
on decode_chars(these_chars)
	copy these_chars to {indentifying_char, multiplier_char, remainder_char}
	set the hex_list to "123456789ABCDEF"
	if the multiplier_char is in "ABCDEF" then
		set the multiplier_amt to the offset of the multiplier_char in the hex_list
	else
		set the multiplier_amt to the multiplier_char as integer
	end if
	if the remainder_char is in "ABCDEF" then
		set the remainder_amt to the offset of the remainder_char in the hex_list
	else
		set the remainder_amt to the remainder_char as integer
	end if
	set the ASCII_num to (multiplier_amt * 16) + remainder_amt
	return (ASCII character ASCII_num)
end decode_chars

As a person totally lost when it comes to programming, would you be able to help? I thought about having the recurring event change their starting date to current date (or today), but have no idea how to go about it. Is there any way to create recurring todo’s from recurring events without having the entire event deleted upon script execution?

Any thoughts? Thanks in advance.

Stan :smiley:

Model: Macbook Pro
Browser: Firefox 3.0.5
Operating System: Mac OS X (10.4)