PDA

View Full Version : Mountain Lion Calendar - exporting calendars as .ics files




Schubi
Mar 22, 2013, 12:10 PM
Hello,

I wonder if anyone can help with AppleScript and knows the latest OSX Calendar functions well.

My goal is to backup each calendar within the Calendar application to a single directory as .ics files.

I've been reading through this post (http://forums.macrumors.com/showthread.php?t=296233) from 2007 and tried to modify the script from xUKHCx without any success. I assume that the Calendar application has changed quite a bit over the last 5 years.

-- Set paths and variables ------------------------------------------------------------------------------------
set timeStamp to do shell script "date \"+%Y%m%d\""
set calendar_folder to POSIX path of (path to home folder from user domain)
set sources_folder to quoted form of (calendar_folder & "Library/Calendars")
set backup_folder to POSIX path of ("Users/ros/Backup/Calendar/")

-- Find all Info.plist files ---------------------------------------------------------------------------------
set infolist to paragraphs of (do shell script "find -f " & sources_folder & "| grep -v “/Contents” | grep Info.plist$")
set calstartlist to {}
set caldestlist to {}
set calnamelist to {}

-- Create a backup folder -------------------------------------------------------------------------------------
try
set destfolder to backup_folder
set fileName to timeStamp
do shell script "mkdir -p " & quoted form of destfolder & "/" & quoted form of fileName
set destFile to destDir & "/" & fileName
end try

-- Search through above results -------------------------------------------------------------------------------
repeat with calname in infolist
set calname to calname as string
set calname to text 1 thru -7 of calname

-- Create a path to the original .ics files -------------------------------------------------------------------
set calstart to quoted form of (text 1 thru -5 of calname & "corestorage.ics")
copy calname to end of calstartlist
set calname to quoted form of calname

-- Read the Info.plist and grab the calendar names ------------------------------------------------------------
try
set calname to (do shell script "defaults read " & calname & " Title")
set caldest to quoted form of (backup_folder & calname & ".ics")
copy calname to end of calnamelist
copy caldest to end of caldestlist
end try
end repeat

set calexport to choose from list calnamelist with multiple selections allowed
repeat with calname in infolist
set calname to calname as string
set calname to text 1 thru -7 of calname
set calstart to quoted form of (text 1 thru -5 of calname & "corestorage.ics")
copy calname to end of calstartlist
set calname to quoted form of calname
try
set calname to (do shell script "defaults read " & calname & " Title")
set caldest to quoted form of (backup_folder & calname & ".ics")
if calname is in calexport then
do shell script "ditto " & calstart & " " & caldest
end if
end try
end repeat

The end result should be as in the attached screenshot. Any help is greatly appreciated - after 2 days of searching through Google and other forums.. I'm out of skills and ideas!



kryten2
Mar 22, 2013, 08:15 PM
My goal is to backup each calendar within the Calendar application to a single directory as .ics files.

I couldn't find corestorage.ics on my mac or anything about exporting calendars in iCal's dictionary so I decided to go with GUI scripting. Not the best or most elegant solution but I think it does the job.

property savePath : ""
set timeStamp to do shell script "date \"+%Y%m%d\""
set backup_folder to "/Users/kryten/Backup/Calendar/"
set macPath_Backup_folder to POSIX file backup_folder
try
tell application "Finder"
if not (exists macPath_Backup_folder) then
set macBackup_folderExists to false
my createDirectories(backup_folder, timeStamp)
else
set savePath to backup_folder & timeStamp
--log savePath
set macBackup_folderExists to true
end if
end tell
end try

on createDirectories(theFolders, theDate)
set savePath to do shell script "mkdir -vp " & quoted form of theFolders & quoted form of theDate
--log savePath
end createDirectories

tell application "iCal"
activate
delay 2
set everyCalendar to every calendar
set countLoop to 0
repeat with aCalendar in everyCalendar
set calendarName to name of aCalendar
set countLoop to countLoop + 1
--log countLoop
tell application "System Events"
tell application process "iCal"
click menu item "Export…" of menu 1 of menu item "Export…" of menu 1 of menu bar item "File" of menu bar 1
delay 0.5
--keystroke aCalendar's name
keystroke calendarName
delay 0.2
if countLoop = 1 then
keystroke "g" using {command down, shift down}
delay 0.2
if macBackup_folderExists is false then
keystroke third paragraph of savePath
else
keystroke savePath
end if
--delay 0.5
keystroke return
--delay 0.5
end if
keystroke return
end tell
end tell
end repeat
end tell


Note : Make sure Enable access for assistive devices is checked in Universal Access of System Preferences. Tested on Snow Leopard with iCal 4.0.4. YMMV.

Schubi
Mar 22, 2013, 10:26 PM
Hello kryten2,

Thanks for looking into this. The script is what I've been looking for - apart for some small problems.

1) It does not create a new backup folder ('YYYYMMDD').

2) The script runs through the actions very quickly and stumbles upon an event creating a calendar backup called Employmentnment.ics instead of Employment.ics

3) All backup files have the same file size. Clicking a manual 'File -> Export -> Export...' in Calendar however saves the calendars with their correct file size.

4) The Calendar menu has slightly changed in Calendar 6.0 (1648) so I made the following adjustments to 'click menu item'. Here's the whole script:
property savePath : ""
set timeStamp to do shell script "date \"+%Y%m%d\""
set backup_folder to "/Users/ros/Backup/Calendar/"
set macPath_Backup_folder to POSIX file backup_folder
try
tell application "Finder"
if not (exists macPath_Backup_folder) then
set macBackup_folderExists to false
my createDirectories(backup_folder, timeStamp)
else
set savePath to backup_folder & timeStamp
--log savePath
set macBackup_folderExists to true
end if
end tell
end try

on createDirectories(theFolders, theDate)
set savePath to do shell script "mkdir -vp " & quoted form of theFolders & quoted form of theDate
--log savePath
end createDirectories

tell application "Calendar"
activate
delay 2
set everyCalendar to every calendar
set countLoop to 0
repeat with aCalendar in everyCalendar
set calendarName to name of aCalendar
set countLoop to countLoop + 1
--log countLoop
tell application "System Events"
tell application process "Calendar"
click menu item "Export…" of menu 1 of menu item "Export" of menu 1 of menu bar item "File" of menu bar 1
delay 0.5
--keystroke aCalendar's name
keystroke calendarName
delay 0.2
if countLoop = 1 then
keystroke "g" using {command down, shift down}
delay 0.2
if macBackup_folderExists is false then
keystroke third paragraph of savePath
else
keystroke savePath
end if
--delay 0.5
keystroke return
--delay 0.5
end if
keystroke return
end tell
end tell
end repeat
end tell


I've created a screencast that shows what the script does (31MB, .mov): http://195.30.252.25/files/dmp/ScreencastCalendar.mov

kryten2
Mar 23, 2013, 10:39 AM
Hello kryten2,

Thanks for looking into this. The script is what I've been looking for - apart for some small problems.

1) It does not create a new backup folder ('YYYYMMDD').

2) The script runs through the actions very quickly and stumbles upon an event creating a calendar backup called Employmentnment.ics instead of Employment.ics

3) All backup files have the same file size. Clicking a manual 'File -> Export -> Export...' in Calendar however saves the calendars with their correct file size.

4) The Calendar menu has slightly changed in Calendar 6.0 (1648) so I made the following adjustments to 'click menu item'. Here's the whole script:

I've created a screencast that shows what the script does (31MB, .mov): http://195.30.252.25/files/dmp/ScreencastCalendar.mov

About 5 minutes after posting my reply and thinking about it a little bit harder I knew this script was no good.

1) Make a new script with everything except the tell application iCal block and look at the replies and events in Applescript Editor why it fails to create a new backup folder. Uncomment the log statements for more feedback.

2) Yeah it runs fast on your system. Increase the delays.

3) Here's the real problem. The script loops through every calendar but doesn't really export the right calendars. Mainly because iCal doesn't understand( on Leopard and Snow Leopard anyway) the selection keyword.
I'll look into UI browser to select them with GUI scripting. Unfortunately I'm not on Mountain Lion so I can't really test anything.

I'll see if I can make some time to get ML running on my hack to come up with a satisfying solution to your problem. There's on thing you can do. Can you look into the following path if you see a corestorage.ics file?

/Users/ros/Library/Application Support/iCal/Sources/2B835489-C867-4DBB-9AFC-5986AA3F58F4.calendar/

The id part is just an example and should be different on your mac. If it's there and also the info.plist file the first script uses it should be possible to make the first script you posted work after you change the sources_folder to the right path eg /Users/ros/Library/Application Support/iCal

Schubi
Mar 24, 2013, 06:59 AM
Thanks for the effort you are putting into helping me!

Make a new script with everything except the tell application iCal block and look at the replies and events in Applescript Editor why it fails to create a new backup folder. Uncomment the log statements for more feedback.

Modified script running with logging activated:
property savePath : ""
set timeStamp to do shell script "date \"+%Y%m%d\""
set backup_folder to "/Users/ros/Backup/Calendar/"
set macPath_Backup_folder to POSIX file backup_folder
try
tell application "Finder"
if not (exists macPath_Backup_folder) then
set macBackup_folderExists to false
my createDirectories(backup_folder, timeStamp)
else
set savePath to backup_folder & timeStamp
log savePath
set macBackup_folderExists to true
end if
end tell
end try

on createDirectories(theFolders, theDate)
set savePath to do shell script "mkdir -vp " & quoted form of theFolders & quoted form of theDate
log savePath
end createDirectories

From the Events/Replies section in AppleScript Editor (although the folder '20130324' does NOT exist in /Users/ros/Backup/Calendar/):
tell current application
do shell script "date \"+%Y%m%d\""
--> "20130324"
end tell
tell application "Finder"
exists file "Main:Users:ros:Backup:Calendar:"
--> true
(*/Users/ros/Backup/Calendar/20130324*)
end tell
Result:
true



Can you look into the following path if you see a corestorage.ics file?

I could not find any 'corestorage.ics' file at all on my system:
[localhost]$ sudo find / -type f -name corestorage.ics
Password:
[localhost]$

But I found where all the calendars are stored. I sync my calendars to an OwnCloud server so the path is 'caldav' related. Comments in brackets () right to the file listing:

Calendar Root Directory
/Users/ros/Library/Calendars/
drwxr-xr-x 22 ros staff 748 Mar 18 22:38 8E83A3AD-8EEF-4428-8203-2A4C67565E30.caldav (All my personal calendars)
drwxr-xr-x 5 ros staff 170 Oct 6 17:01 5DD3C270-985B-4C7A-AE57-399DB46CDDE6.calendar (The 'Birthdays' calendar)

drwxr-xr-x 5 ros staff 170 Oct 6 17:14 750D9A10-25FD-4CB7-9FA9-0464084CDBF5.caldav (NotificationCollection)
drwxr-xr-x 4 ros staff 136 Oct 6 15:54 C60C3148-EB8F-4929-B59E-9B0B8C6EF0EB.calendar (Event Reminders)
-rw-r--r--@ 1 ros staff 7356416 Mar 24 12:25 Calendar Cache (Binary File)
drwxr-xr-x 39 ros staff 1326 Mar 24 12:02 Calendar Sync Changes (Temp files)
-rw-r--r-- 1 ros staff 182 Oct 6 17:14 Email Cache.plist (Empty file)
drwxr-xr-x 2 ros staff 68 Oct 6 16:16 Incoming (Empty folder)

Calendar Directory for all my personal calendars
/Users/ros/Library/Calendars/8E83A3AD-8EEF-4428-8203-2A4C67565E30.caldav/
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 04950D7E-2BFA-4239-A349-24A573513B9E.calendar (Education)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 3CAF1ED2-BE78-4A46-BC4D-E6C2BDD05B49.calendar (Health)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 3F438A8D-EDDB-4FC0-861D-E5B2550A6D55.calendar (Rest)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 4605849B-95A7-47B0-AEF3-39D6D04B7071.calendar (Food)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 7CE2B1AD-C7E1-4DC8-86B6-62382364F678.calendar (Location)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 86C2E0A3-46E2-4576-8D40-0027E409F593.calendar (Holidays)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 94764D88-4D2E-4EF8-AE2D-042D805D847D.calendar (Socialising)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 9C483337-B54C-4656-9AD4-D209418EDA33.calendar (Employment)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 BEE64618-8AD9-4213-994C-A2EDA573082D.calendar (Entertainment)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 CCF565CD-053B-479F-BE8D-EAC00681EA05.calendar (Transport)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 D8B8EEC6-7963-4B9C-B759-97EA3EBD313C.calendar (ToDo)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 DE09DCB3-11E5-481B-9825-8CC00EEDAD2B.calendar (Hygiene)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 E4FD5C67-3BE5-4C0A-841F-E7AD244386B0.calendar (Sports)
drwxr-xr-x 4 ros staff 136 Mar 18 22:38 FE588D5A-A7A0-49B6-AA7A-1926468641ED.calendar (Relationship)

drwxr-xr-x 121 ros staff 4114 Mar 23 18:43 Attachments (Empty folder)
drwxr-xr-x 2 ros staff 68 Feb 13 10:37 Inbox (Empty folder)
-rw-r--r--@ 1 ros staff 1953 Mar 24 12:10 Info.plist (CalDav setup details)
drwxr-xr-x 4 ros staff 136 Oct 8 17:09 LocalDefaultAlarms (Alarm related details)
drwxr-xr-x 2 ros staff 68 Oct 6 17:14 NotificationCollection (Empty folder)
drwxr-xr-x 4 ros staff 136 Oct 6 17:02 ServerDefaultAlarms (Alarm related details)

Example Calendar Details for Calendar 'Education'
/Users/ros/Library/Calendars/8E83A3AD-8EEF-4428-8203-2A4C67565E30.caldav/04950D7E-2BFA-4239-A349-24A573513B9E.calendar/
drwxr-xr-x 91 ros staff 3094 Mar 19 23:05 Events
-rw-r--r-- 1 ros staff 1169 Mar 24 11:45 Info.plist

Example Info.plist for Calendar 'Education' (snipped details)
/Users/ros/Library/Calendars/8E83A3AD-8EEF-4428-8203-2A4C67565E30.caldav/04950D7E-2BFA-4239-A349-24A573513B9E.calendar/Info.plist
...
<key>Key</key>
<string>04950D7E-2BFA-4239-A349-24A573513B9E</string>
...
<key>Title</key>
<string>Education</string>
...

Calendar Events for Calendar 'Education' (just a short selection of hundred of .ics files):
/Users/ros/Library/Calendars/8E83A3AD-8EEF-4428-8203-2A4C67565E30.caldav/04950D7E-2BFA-4239-A349-24A573513B9E.calendar/Events/
-rw-r--r-- 1 ros staff 1041 Mar 18 22:38 042597A8-26B1-4DAF-8B40-56314C666062.ics
-rw-r--r-- 1 ros staff 1041 Mar 18 22:38 0A69923B-2EEF-4D6A-A187-5CFB94C30026.ics
-rw-r--r-- 1 ros staff 1085 Mar 18 22:38 0F476A80-364D-456C-BE31-A894334A4A31.ics
-rw-r--r-- 1 ros staff 1039 Mar 18 22:38 158904AF-C32B-4814-B006-DC704D0465BD.ics
-rw-r--r-- 1 ros staff 1041 Mar 18 22:38 18A199AE-92DC-453F-935A-8B162711854A.ics
...

kryten2
Apr 16, 2013, 07:18 PM
Thanks for the effort you are putting into helping me!

Sorry it's been a while. Can you try this :

-- Selecting calendars in iCal and export using GUI scripting
-- Change the backup_folder path!
-- Don't click anywhere else when the srcipt is running!

set timeStamp to do shell script "date \"+%Y%m%d\""
set backup_folder to "/Users/kryten/Backup/Calendar/"
do shell script "mkdir -p " & quoted form of backup_folder & quoted form of timeStamp
set savePath to backup_folder & timeStamp

-- Begin script
tell application "Calendar"
launch
activate
delay 1
set calNames to name of every calendar
end tell

tell application "System Events"
tell process "Calendar"

-- Left-hand menu of iCal, containing named calendars
set myOutline to outline 1 of scroll area 1 of splitter group 1 of splitter group 1 of splitter group 1 of window "Calendar"
set allRows to rows of myOutline

-- List of named calendars
set calNameVals to value of text field of every row in myOutline

-- For all named calendars, check whether it's the same name as one of calNames
-- If so, export that calendar.

set countLoop to 0
repeat with i from 2 to (count calNameVals)
set countLoop to countLoop + 1
set calName to item 1 of item i of calNameVals
repeat with myName in calNames
if (myName as string = calName as string) then
tell row i of myOutline
select
delay 1
end tell
click menu item "Export…" of menu 1 of menu item "Export" of menu 1 of menu bar item "File" of menu bar 1
delay 2
if countLoop = 1 then
keystroke "g" using {command down, shift down}
delay 1
keystroke savePath
delay 1
click button "Go" of sheet 1 of sheet 1 of window 1
end if
click button "Export" of sheet 1 of window 1
end if
end repeat
end repeat
end tell
end tell

Schubi
Apr 21, 2013, 09:59 AM
Sorry it's been a while. Can you try this :
Like a clock-work! Thank you very much!

I'm pretty sure this will be helpful to a lot of other people as well - pity that such a basic function isn't implemented in Mountain Lion!

bcarpenter
May 9, 2013, 08:50 AM
This is great. Thanks!

I'd been previously saving my local (non-iCloud) calendars using an Applescript using a Calendar Archive, so scripting was relatively easy. Your method should also work for iCloud-based calendar backups, since these calendars aren't included in Calendar Archives.

However, I have struck a problem trying to implement your script if the Calendar application is actively updating subscribed calendars. In this situation, the window isn't called "Calendar. It's called "Calendar — Updating…".

How would I modify the script to select either window, bearing in mind that the name may change midway through the script execution?

Thanks.

Schubi
Nov 1, 2013, 05:34 AM
As this script doesn't seem to work under OSX Mavericks anymore, I'ver created a new thread to discuss a solution:

Mavericks Calendar - exporting calendars as .ics files (http://forums.macrumors.com/showthread.php?p=18281548)