iCal and the Import... command

Hi there–some time lurker, first-time poster…

I’ve written a script that will sync an ics file from MS Outlook and my Entourage calendar into iCal. The data transfer portions of the script are working great. I even used a little UIScripting for the first time. However, I’m having trouble with some of my post-processing.

The Import command in iCal allows you to import events from a default Entourage identity, and it’s effective for my purposes. However, it takes a long time to run once invoked, and my script just pushes on through it’s post-import processing before the importing has completed.

There is a floating progress bar window that comes up while iCal does the import. It changes state part-way through the process, as if the first part of the import is reading from Entourage and the second part is writing to iCal. My script seems to continue it’s execution while the writing to iCal is happening.

I’d welcome any suggestions on how to postpone or suspend the execution of my AppleScript until the iCal import from Entourage has completed. First, the script, then some of the things I’ve tried already:


(*iCal Color Presets
{514, 21074, 54484} = Blue = Home
{62965, 30840, 514} = Orange = Work
{45232, 10023, 44718} = Pink
{58853, 5911, 5911} = Red
{11308, 41377, 2827} = Green
{18761, 11051, 41377} = Purple*)

global calendarFile
global dialogReturn
global allCalendars

on run
	set calendarFile to "BRAD_USB:OUTLOOK TRANSFER:calendar:Calendar.ics"
	set dialogReturn to ""
	tell application "iCal"
		set allCalendars to the name of every calendar
		if allCalendars contains "Work" then set name of calendar "Work" to "Work-old"
		if allCalendars contains "Home" then set name of calendar "Home" to "Home-old"
	end tell
	
	try
		main()
--THIS IS THE CODE THAT EXECUTES BEFORE ICAL HAS FINISHED IMPORTING
		tell application "iCal"
			if allCalendars contains "Work-old" then delete calendar "Work-old"
			if allCalendars contains "Calendar" then
				set name of calendar "Calendar" to "Work"
				set color of calendar "Work" to {62965, 30840, 514}
			end if
			if allCalendars contains "Home-old" then delete calendar "Home-old"
			if allCalendars contains "Entourage" then
				set name of calendar "Entourage" to "Home"
				set color of calendar "Home" to {514, 21074, 54484}
			end if
		end tell
	on error errNum
		display dialog "Error: " & errNum
		--Oddly, iCal doesn't throw an error back to the script when opening a non-existent file (like if the USB stick isn't mounted) so the following code isn't currently needed
		(*display dialog "Could not find USB Volume. Would you like to choose a file?" buttons {"No", "Yes"} default button "No" with icon caution
		if button returned of result = "Yes" then
			set calendarFile to chooseFile(calendarFile) as string
			main()
		end if*)
	end try
end run

on main()
	importOutlookCalendar(calendarFile)
	set dialogReturn to button returned of fileOkay(dialogReturn)
	if dialogReturn = "No" then
		repeat while dialogReturn = "No"
			set calendarFile to chooseFile(calendarFile) as string
			importOutlookCalendar(calendarFile)
			set dialogReturn to button returned of fileOkay(dialogReturn)
		end repeat
	end if
	importEntourageCalendar()
end main

on importOutlookCalendar(calendarFile)
	tell application "iCal"
		activate
		open file calendarFile
		set allCalendars to the name of every calendar
	end tell
end importOutlookCalendar

--HERE'S THE ACTUAL IMPORT CODE.
on importEntourageCalendar()
	tell application "System Events"
		with timeout of (10 * 60) seconds
			tell process "iCal"
				set frontmost to true
				click menu item "Import." of menu "File" of menu bar 1
				repeat until window "Import" exists
				end repeat
				click radio button "Import Entourage data" of radio group 1 of window "Import"
				keystroke return
--POSSIBLE SOLUTION CODE (FROM POST) HAS BEEN TRIED HERE
			end tell
		end timeout
	end tell
end importEntourageCalendar

on fileOkay(dialogReturn)
	activate
	display dialog "Was Import Successful?" buttons {"No", "Yes"} default button "Yes" with icon caution
end fileOkay

on chooseFile(calendarFile)
	activate
	choose file with prompt "Choose a different .ics file" without invisibles
end chooseFile

Things I’ve tried include:

  1. ‘delay x’: The problem is that I don’t want to wait longer than I have to, and also, the import process takes varying amounts of time, so a hard-coded delay is not a great solution.

  2. ‘repeat while’: None of the tests I’ve come up with ever resolve to TRUE, so the repeat never kicks in. The tests I’ve tried are ‘window “” exists’, ‘while busy indicator 1 of window “” exists’, ‘while (count of iCal’s windows)>startingCount’. The window with the busy/progress indicator has no title as revealed by the UIElement Inspector.

Anyone have ideas?

Thanks,
Brad

If you look in ~/Library/Caches/Metadata/iCal/ you’ll see a bunch of folders (assuming you have several calendars), and if you look at them in list view sorted by date modified, you’ll discover which one changes when you import (they’ve got names like “9B5D75D9-E2BE-40DE-BFFF-57D2B3DAB130”).

You might try something like this (which may need fiddling with the delay:

-- insert this command after the import command
checkStableSize(destinationFilePath) -- change to correct variable name

-- This works with both Files or Folders

-- Add this handler after main script.

-- Handler to delay until a file is fully loaded.
on checkStableSize(myFile)
	set sizeThen to size of (info for myFile)
	repeat
		tell application "Finder" to update myFile
		delay 1 -- seconds (set to longer if needed)
		set sizeNow to size of (info for myFile)
		if sizeNow - sizeThen = 0 then exit repeat
		set sizeThen to sizeNow
	end repeat
end checkStableSize

Hi,

I can’t check this, but you might send another event to iCal for something. Since iCal is busy, the script might wait until it’s not busy before getting back a reply. I don’t think the scirpt would interupt it, but not sure.

gl,

Thanks for the ideas. I’ve done some more testing (and more is yet required before I reach a final solution), but here’s what I found out…

Adam, I liked your idea, but ran into the same problem as I had originally. The Caches folders don’t start updating until the “2nd stage” of the Entourage import. Whatever the break is between the 1st & 2nd stages still allows the remainder of the script to execute, just like in my original code.

kel, I think I’m actually already sending other events to iCal. The problem is that these events are being processed before the import from Entourage process completes instead of waiting until the importing is over.

However, I discovered that no matter what state Entourage is in, when iCal begins its ‘import from Entourage’ process, Entourage either activates or launches and remains running until the import completes (from looking at what is happening in the Dock). Once the import process is totally finished, Entourage is quit. That means I should be able to check for the existance of the Entourage process as my delay, right?

Best,
Brad

Sadly…no dice. The following code for my importEntourage routine yields the same results as described above:

on importEntourageCalendar()
	tell application "System Events"
		with timeout of (10 * 60) seconds
			tell process "iCal"
				set frontmost to true
				click menu item "Import." of menu "File" of menu bar 1
				repeat until window "Import" exists
				end repeat
				click radio button "Import Entourage data" of radio group 1 of window "Import"
				keystroke return
----- THIS IS THE NEW CODE ---
				tell application "System Events"
					repeat while process "Microsoft Entourage" exists
						delay 1
					end repeat
				end tell
------------------------------------------
			end tell
		end timeout
	end tell
end importEntourageCalendar

When the ‘import Entourage’ command of iCal reaches the 2nd stage, the above subroutine returns to the main() subroutine, which immediately jumps back to the run() handler, executing my deletion and color code, except that since the new calendar import hasn’t completed, I get left in an odd state.

Still looking for any other ideas…

Thanks,
Brad

UPDATE: The following entry was in the Event log when trying to debug the above code:

exists process “Microsoft Entourage”
false

Except I can clearly see that Entourage is running. Do I have a sneaky syntax problem?