Script says daily agenda but ignores reoccurring events.

This script reads out your daily agenda but ignores reoccurring events, please help. I am so new to AppleScript that I can take simple code and rearrange it for my needs, kind of. :wink: However, this code is a little complicated and I am having problems with it. My first problem was that I found this script on the internet but I didn’t notice that it wasn’t working properly until I had reoccurring events in my schedule. My second problem is that I can’t find where I got the original code from. :rolleyes: I guess I should use that bookmarks button a little more.

When I run this script I find that it will say all of the events but it will not say the reoccurring events. I would also like this script to be able to read my todos to me but it does not. I realize that I am in a bit over my head but I appreciate any help. :smiley:


set now to current date
set thisMorning to now - (time of now)
set thisEvening to thisMorning + 1 * days - 1
set foundOne to false
set det to ""
set today to now - (time of now)
set bname to ""
set dahead to 2
set dbefor to 30


tell application "iCal"
	set foundOne to false
	repeat with thisEvent in (every event of calendar "Birthdays")
		tell thisEvent
			set thedate to start date
			if (month of thedate = month of now and day of thedate = day of now) then
				set bname to summary
				try
					if not foundOne then
						say "today is" & bname
						set foundOne to true
					else
						say "today is also" & bname
					end if
				end try
			end if
		end tell
	end repeat
	set foundOne to false
	
	say "Your agenda ..."
	repeat with thisCalendar in calendars
		repeat with thisEvent in ((every event of thisCalendar) whose ((start date ≥ thisMorning and start date ≤ thisEvening) or (end date ≥ now and start date ≤ thisMorning)))
			set foundOne to true
			tell thisEvent
				set thedate to start date
				set allday to allday event
				try
					if not allday then set theEventSummary to ("At " & time string of thedate & ", " & summary)
					if allday then set theEventSummary to ("Today is " & summary)
					set Esumm to summary
				on error
					set theEventSummary to " No summary - " & time string of thedate
					set Esumm to ""
				end try
				
				try
					set theEventDescription to description
					set Edesc to description
					log theEventDescription
				on error
					set theEventDescription to ""
					set Edesc to ""
				end try
			end tell
			say theEventSummary
			say theEventDescription
		end repeat
	end repeat
	set Esumm to ""
	set Edesc to ""
	
	repeat with calens in every calendar
		if (count todos of calens) > 0 then
			repeat with tasks in (todos of calens whose (due date > today - dbefor * days) and (due date ≤ (today + dahead * days)))
				try
					completion date of tasks is missing value
				on error
					try
						set foundOne to true
						set det to det & "... The pending tasks are..." & return
						set dd to due date of tasks
						set d2 to month of dd & " " & day of dd
						set dd to short date string of dd & " "
						if character -2 of dd is ":" then set dd to dd
					on error
						set dd to tab
					end try
					try
						set Esumm to det & summary of tasks & tab & "in " & d2 & tab & "..."
					on error
						set Esumm to ""
					end try
					try
						set Edesc to description of tasks & ""
					on error
						set Edesc to ""
					end try
					try
						set d3 to "" & d2 & ": " & summary of tasks
					on error
						set d3 to ""
					end try
				end try
			end repeat
		end if
	end repeat
	say Esumm
	say Edesc
	if foundOne then
		say " and that's all."
	end if
end tell

if not foundOne then say "There are no events scheduled"

end

The problem is this: iCal stores only one instance of recurrent events on the date of their creation (not necessarily on a day of the recurrence), and it generates all subsequent instances “on the fly” each time you open a calendar. It does not do this when when a script retrieves events from iCal, however, so the script must do it.

The particulars of a set of repeating events are stored in the recurrence property of the original event as a semicolon-delimited Unicode text string. Like too many applications, however, there are not a fixed set of attributes with missing values for those not asserted. Instead, the string can take many forms using a labeling scheme to identify them (It would have been nicer for scripters if iCal had used a record here, but that would not have met the international iCalendar standard for calendar interoperability (RFC 2445). Compliant calendars use the ics extension for their data and certainly not all of them use records the way AppleScript does.

A storage scheme like iCal’s, which meets an international standard set up for easy parsing in a variety of environments, time zones and languages, means that a script to collect events must extract all of the events in a calendar rather than filter for those in a specified date range (events between two dates) because recurrences for which the first entry precedes the range’s beginning will not appear in the results of such a filtration - as mentioned, they are not computed for the AppleScript request. To show recurrences in a given date range where the first instance precedes the date range, your script must compute the dates on which they would fall in that range from the recurrence properties attached to the first (and only real) instance of the recurring event.

Not a trivial pursuit because parsing the recurrence string (with its very rich range of possibilites) is not trivial and a large number of handlers are required to do it.

Well since this goes beyond my knowledge of AppleScript I think I will abandon this endeavor for now. Thanks for your help. :smiley:

Thanks for the description Adam, I had been wondering about this. Is there a generally used solution for “please tell me all the events occuring at time x” with AppleScript? I understand from what you said that to actually do it in AppleScript would require not only iterating through every event in every calendar but doing fairly complex math on the repeating ones, so presumably doing it in an interpreted language like that’s not very feasable - is there perhaps a compiled solution that outputs text that we could grab into an AppleScript program to use, or similar?

My particular problem at the moment basically requires answering “What events are happening now?”, a slightly different problem from the OP’s.

Regards,
Ricky

Hi, Ricky;

Collecting recurrent events from a sweep through all of them doesn’t take long – I do it every day with a “Birthdays” calendar, and a “Medical” calendar. The clue is to limit the types of recurrences you use so that parsing them is straight-forward.

Here’s an example for my Birthdays calendar where they are all all day events and repeat in a simple way – once per year. If you respond with the types of recurrences you are using, perhaps I can help more.

-- This script calculates the days remaining before the next four birthdays in an iCal calendar named "Birthdays".
-- Each birthday is entered as an all day event
-- The script uses Growl to display the result.

-- Get today's date with time set to midnight. Later days to go subtractions must have a time of day in common or differences can be off by one day.
set today to current date
set time of today to 0 -- seconds after midnight

--  Get the Birthday Date List "bDays" and Birthday Name List "bName", correct for GMT, then subtract today after correcting year.
tell application "iCal"
	close window 1
	set bdCals to every calendar whose title contains "Birthdays"
	set bCal to first item of bdCals
	set toGo to {}
	set who to {}
	set when to {}
	-- Collect the date/name lists
	set bCount to count events of bCal
	repeat with n from 1 to bCount
		-- Start date is the birthday for an all day event. 
		-- Summary should be the person's name.
		-- iCal stores times of events in GMT even though it presents them in local time. 
		-- Times must be shifted back to a midnight base before days to go are calculated.
		tell event n of bCal to set {tName, tDate} to {summary of it, start date of it}
		-- adjust for GMT offset
		tell tDate
			if time of it = 75600 then -- 9:00 PM if ADST
				set time of it to (time of it) + 10800
			else -- 8:00 PM if AST
				set time of it to (time of it) + 14400
			end if
			-- adjust the calendar year of the birthday ahead of now
			repeat until (it - today) > 0
				set year of it to (year of it) + 1
			end repeat
			-- Calculate days to go to next birthday
			set daysLeft to ((it - today) / days) as integer
			--set item n of tDate to daysLeft
			set end of toGo to daysLeft
			set end of who to tName
			set end of when to it
		end tell
	end repeat
	--quit
end tell

-- Sort by days to go
sort_items(toGo, who, when)

-- Get first four
set msg_C to " days until "
set BDNotes to ""
set r to return
set rr to r & r
set sp to space
set ap to ASCII character 240
repeat with mm from 1 to 5
	set msg_A to (item mm of toGo) as text
	set msg_B to (item mm of who)
	set msg_D to date string of (item mm of when)
	-- add possessives for names: soAndso's Birthday
	if last character of msg_B = "s" then
		set msg_B to msg_B & "'"
	else
		set msg_B to msg_B & "'s"
	end if
	if msg_B = "Anniversary's" then set msg_B to "Our Anniversary"
	set tBDNote to msg_A & msg_C & msg_B & " on" & r & ap & sp & sp & msg_D
	set BDNotes to BDNotes & tBDNote & rr
end repeat

Growl_It("Birthdays Coming Soon", BDNotes)

to sort_items(sortList, SecondList, thirdList)
	tell (count sortList) to repeat with i from (it - 1) to 1 by -1
		set s to sortList's item i
		set r to SecondList's item i
		set q to thirdList's item i
		repeat with i from (i + 1) to it
			tell sortList's item i to if s > it then
				set sortList's item (i - 1) to it
				set SecondList's item (i - 1) to SecondList's item i
				set thirdList's item (i - 1) to thirdList's item i
			else
				set sortList's item (i - 1) to s
				set SecondList's item (i - 1) to r
				set thirdList's item (i - 1) to q
				exit repeat
			end if
		end repeat
		if it is i and s > sortList's end then
			set sortList's item it to s
			set SecondList's item it to r
			set thirdList's item it to q
		end if
	end repeat
end sort_items

on Growl_It(gTitle, gMessage)
	tell application "GrowlHelperApp"
		notify with name "Next4BD" title gTitle description (return & gMessage) application name "Birthdays" with sticky
	end tell
end Growl_It
----

Hi.

I’ve just posted a fairly comprehensive (and fairly long!) script to get the next recurrence date of an iCal event in Code Exchange, if it’s of any interest.