Move older iCal events

http://bbs.macscripter.net/viewtopic.php?id=25634

I’ve been trying to modify this last script with no luck. What I’m trying to do is move all events older than a month to a different calendar. I have a user who is a crazy big calendar user and so far, the server apps don’t like it. I’m trying to make the older information be a local calendar and just have new and future events be on the server.

Any ideas or full scripts would be great. I’ve gotten some general scripts for moving and deleting to work, it’s mainly the finding the list of those older events that I haven’t figured out.

Thanks very much, Ian

Do you have to deal with recurring events? They pose the problem that they are only listed once on the day they are posted and calculated in real time thereafter.

If they’re all of one flavor (of the many ways recurrences can be scheduled) it’s not too bad, but if they’re of many types it gets rather complex. Nigel Garvey is the guru of recurrences.

I’m sure there are some recurring events, but the script I linked to seems to deal with that. But, since you said they can happen multiple ways, maybe it doesn’t deal with all of them?

As far as I can tell, if I dealt with all of the non-recurring events, that would get me down into well below the amount of events that iCal server or Kerio would complain about. This user tends to do a lot of varied things, so the amount of recurring events is probably close to zero.

He also wants to keep the old stuff, so I don’t have the option of “deleting events older than __” in the prefs.

I searched through the ical posts of the user you mentioned, but I didn’t see anything that helped out.

Another approach that occurs to me is to duplicate all recurring events in the kept portion of the calendar, and move all others to the alternate archive calendar. I’ll have a look at that (without promises).

Recurring events can be a little complicated from the point of view of deciding whether they’ve expired or not. If there aren’t that many, a quick approach would be (as suggested above) to leave them in and then deal with them by hand or with another script once the non-recurring events have been moved. Recurrence instances that have been individually modified look like non-recurring events and can be treated as such.

The fastest simple method seems to be to use a single bulk command in iCal. However, if there are thousands of events, this is still likely to take longer than the two minutes or so that AppleScript allows for a command to time out, so a ‘with timeout’ block is needed. The script below moves non-recurring events whose ‘end date’ comes before midnight one calendar month ago from ‘calendar 1’ to ‘calendar 2’. It waits for up to an hour before deciding that the ‘move’ command isn’t going to return:

on addMonths(oldDate, m) -- add m calendar months to oldDate,
	copy oldDate to newDate
	set {y, m} to {m div 12, m mod 12}
	if (m < 0) then set {y, m} to {y - 1, m + 12}
	set newDate's year to (newDate's year) + y
	if (m is not 0) then tell newDate to set {day, day} to {32 * m, day}
	if (newDate's day) is not oldDate's day then set newDate to newDate - (newDate's day) * days
	return newDate
end addMonths

set cutoffDate to addMonths(current date, -1)
set cutoffDate's time to 0

with timeout of hours seconds
	tell application "iCal"
		move (events of calendar 1 whose end date comes before cutoffDate and recurrence is "") to end of events of calendar 2
	end tell
end timeout

This will of course tie up both iCal and the application running the script for as long as it takes to complete.

Thank you! That looks like it might do it.

What is the normal type of results you see in either applescript or automator when they run out of memory? I was trying to do some of this culling in automator last night (unsuccessfully) and it was crashing iCal. Too many events to keep in mind until it hits the delete command?

It looks like this script goes through and finds a list before acting, is it going to have the same problems once it gets a lot of these into memory? I’ll try it out, so I might be the one answering my question.

And thanks! Ian

I don’t know how many events you need to move, of course. I tested the script with 5001 events (4969 of which were moved) in an old version of iCal on an old machine running Jaguar. It took eight and a quarter minutes to finish, during which time iCal was totally unresponsive because it was locked into that one ‘move’ command. I also tested the principle on a faster machine running iCal 2.0.5 in Tiger and presume that the process would finish quite a bit sooner there.

It’s not a list in the AppleScript sense. The script gives one command to iCal to move any events matching the reference to the destination. It never gets to see the events itself. How iCal handles them is its own little secret. :slight_smile:

If there are too many events to move this way, you should be able to get iCal to move them, say, 5000 at a time.

I wasn’t watching the time, but what I had happen was a timeout error in Script Editor (I just hit run in there as opposed to making an app out of it, is there a difference) and iCal was hung. It was possibly a half hour, but I think more likely closer to an hour in. No events got moved.

I’ve tried bumping the months up to 48, and it ran fine there. So I went down progressively and got to I believe 33 months before I got the same behavior. Is it possibly corruption or malformed events that I’m hitting, not something to do with how many events are on the calendar? I still have no events moved at this point. Is there a way to script dealing with 100 events and then loop again looking for more? Or even go event by event in each loop?

I’m looking into corruption possibilities right now, but trying again lowering the month count 1 at a time.

Thanks again, Ian

Not finding any corrupt or weird entries. I actually scrolled through the whole .ics file that I imported and didn’t find anything strange. When I was doing that I found out that the finder icons and coverflow show the event info now. Don’t know why that would be useful, but it’s definitely interesting.

Is there a way to have a Finder AppleScript search through contents of files? Or maybe I should do a shell script that monthly looks for something like DTEND:(currentDate -1month) and moves the individually stored .ics files to the other calender, trashes the cache, and restarts iCal? That would at least work for right now, but I believe when the calendar is on the server, those events will just get recreated once the server sees them missing.

On the chance that someone is a shell script person here, what’s the variation on this shell script that might work?
mv grep -l "DTEND;TZID=US/Pacific:2007" * …/2007events (ran from the folder that contains the .ics files)

If you were using the one-hour timeout in the script I posted, it will probably have been exactly an hour. It could be that iCal needs more than that to process all the old events you have, or indeed it could be that it just became bloated and seized up. :confused:

Presumably you mean you changed the filter so that it only picked up events that were more than 48 months old. There’d be fewer events to move in that case, which would ease the congestion.

It takes quite a bit longer to complete, but on the other hand, it does complete. :wink: Handling the events one at a time takes forever. Handling them one hundred at a time nominally looks like this:

on addMonths(oldDate, m) -- add m calendar months to oldDate,
	copy oldDate to newDate
	set {y, m} to {m div 12, m mod 12}
	if (m < 0) then set {y, m} to {y - 1, m + 12}
	set newDate's year to (newDate's year) + y
	if (m is not 0) then tell newDate to set {day, day} to {32 * m, day}
	if (newDate's day) is not oldDate's day then set newDate to newDate - (newDate's day) * days
	return newDate
end addMonths

set cutoffDate to addMonths(current date, -1)
set cutoffDate's time to 0

with timeout of hours seconds
	tell application "iCal"
		set c to (count (events of calendar 1 whose end date comes before cutoffDate and recurrence is "")) div 100
		repeat c times
			move (events 1 thru 100 of calendar 1 whose end date comes before cutoffDate and recurrence is "") to end of events of calendar 2
		end repeat
		move (events of calendar 1 whose end date comes before cutoffDate and recurrence is "") to end of events of calendar 2
	end tell
end timeout

The initial counting of the filtered events here can take a while, whereas simply counting all the events in the calendar is almost instantaneous. It’s also the case with iCals 1 and 2 that if there are less than 100 events matching the filter, the order to move numbers 1 thru 100 will simply move what there are without erroring. So a slightly faster version would be:

on addMonths(oldDate, m) -- add m calendar months to oldDate,
	copy oldDate to newDate
	set {y, m} to {m div 12, m mod 12}
	if (m < 0) then set {y, m} to {y - 1, m + 12}
	set newDate's year to (newDate's year) + y
	if (m is not 0) then tell newDate to set {day, day} to {32 * m, day}
	if (newDate's day) is not oldDate's day then set newDate to newDate - (newDate's day) * days
	return newDate
end addMonths

set cutoffDate to addMonths(current date, -1)
set cutoffDate's time to 0

with timeout of hours seconds
	tell application "iCal"
		repeat (count events of calendar 1) div 100 + 1 times
			move (events 1 thru 100 of calendar 1 whose end date comes before cutoffDate and recurrence is "") to end of events of calendar 2
		end repeat
	end tell
end timeout
I actually scrolled through the whole .ics file that I imported and didn't find anything strange.  When I was doing that I found out that the finder icons and coverflow show the event info now.  Don't know why that would be useful, but it's definitely interesting.

I’m afraid I don’t understand that statement at all. :confused:

In my versions of iCal, the .ics files are the calendars. Moving events means deleting entries in one calendar file and moving them to another.

Directly editing the (UTF8) text in the files would no doubt be the fastest option in terms of running time, but the script would take ages to write. It would have to ensure that iCal wasn’t running and would have to be able to cope with every eventuality in the way the data were presented. For instance, depending on the circumstances, “DTEND:” might be followed simply by an ISO-format date or by an ISO-format date/time with a time zone specifier. Or there may even be a “DURATION:” entry instead. If possible, it’s better to use iCal scripting and let iCal worry about the fine details.

Yeah, I know the .ics files are the calendars. What I was wondering was about having some CLI script looking through the text of the calendars since AppleScript just can’t seem to do it. When I looked through the whole file that was imported nothing looked out of the ordinary, so it seems that moving the physical file as opposed to ical events could be a way around whatever is going on. And I guess what I meant was move the .ics files to the other folders, as each “calendar” in iCal stores it’s info in a separate folder. I’ve done little trials of quitting iCal, moving events, clearing out the cache and then restarting iCal and this works as a way to move events. Tedious as heck by hand, but a CLI script would get a little less tired of it than I do.

At this point I’ve tried enough that I think it’s a corruption problem, not that there are too many events or too much data. So now, I’m looking on that side of things.

Thanks for the scripting help, Ian

My last message was forever ago, just wanted to finalize this entry.

It turns out that there was some sort of corruption on this user’s calendar and one other one. I went through every single way of cleaning out calendars, clearing out cache, resetting sync and every other type of hint surrounding iCal problems that you’ll find. Moved it to other machines, moved it to other accounts, exported, imported, backed up, restored . . . I’m not sure what fixed it, but something, or some combination of all of those things finally fixed the calendar. After that, it syncs fine, and as quick as you could expect a huge calendar to sync. The 2nd one with similar issues I haven’t been able to fix yet. Thankfully, that user doesn’t have much stuff, so I just made her a new calendar.

Thanks for the help, I probably will have a use for these scripting ideas in the future as the two heavy iCal users are probably going to fill the hard drive with all of their events.

Ian

Model: OLPC XO
Browser: Browse
Operating System: Other