iCal Geektool appleScript

This Script works great, it pulls my iCal events sorts them in up-coming event dates then saves them to a text file. Geektool then reads the text file and displays it on my desktop. I was hoping somebody could help me streamline my script. I wrote this for myself, but it should work with anybody’s ical and I commented on just about everything so if you do use this make changes accordingly. I am always open for questions and some help.

--------------------------------------------------------------------------------------------------------------
------------------------------------------FAMILY CALENDER--------------------------------------------------
---------------------------------------------------------------------------------------------------------------

set reminderFolder to "Macintosh HD:Users:Jarred:Documents:Remind:" --set the folder where I will save a txt file the display on desktop with geektool"
set newline to ASCII character 10 ----for those who don't know ascii character 10 is the enter/return button-----
-- Find out if iCal is open so we know whether or not to close it later.
tell application "System Events" ----open iCal
if (exists application process "iCal") then
set iCalOpen to true
end if
end tell
----------sort function get pull events from my ical calendar and put them in next event order---------
on findLeastItem(lst)
tell application "iCal"
set theLeast to start date of item 1 of lst
set theIndex to 1
set iterater to 1
repeat with i in lst
if start date of i ≤ theLeast then
set theLeast to start date of i
set theIndex to iterater
end if
set iterater to iterater + 1
end repeat

return theIndex
end tell
end findLeastItem

on removeItemAtIndex(lst, theIndex)
set newList to {}
set theLength to length of lst
if theLength = 1 then
set newList to {}
else if theLength = theIndex then
set newList to items 1 thru (theLength - 1) of lst
else if theIndex = 1 then
set newList to items 2 thru theLength of lst
else
set newList to items 1 thru (theIndex - 1) of lst & items (theIndex + 1) thru (theLength) of lst
end if
return newList
end removeItemAtIndex
-------sort handlers------------------------
on sortEvents(myList)
set myNewList to {}
repeat until length of myList = 0
set leastIndex to findLeastItem(myList)
set end of myNewList to item leastIndex of myList
set myList to removeItemAtIndex(myList, leastIndex)
end repeat
return myNewList
end sortEvents

on getSecondsOfDays(Ndays)
return ((current date) - (3600 * 24 * Ndays))
end getSecondsOfDays
-------------------------------------------
--And to call this i use 
tell application "iCal"
set weeksHours to every event of calendar "Family" whose start date is greater than my getSecondsOfDays(6)
set weeksHours to my sortEvents(weeksHours)
-----weekhours is the name of the list of the events that were sorted in up and coming event order----
end tell
-------^^^^^^^Notice this is where I specified which calendar I was using------
set theTitles to {} ----the summary of each event will go in this list
set theTimes to {} ----the times of each event will go in this list then be parsed
set writeList to {} ----what I will write to the text file

set thePath to reminderFolder & "War is over " & ".txt" -- Create a file named after the calendar (I had declared a war on this script when my sort function wasn't working, hence the name)
tell application "iCal"
set allEvents to weeksHours
repeat with theEvent in allEvents -- Grab all the summaries and dates of each event from ical (repeats a single grab for each event)
set stDate to start date of theEvent
if stDate is greater than (current date) then
if stDate is less than (current date) + 14 * days then ----------ONLY SHOWS EVENTS IN THE NEXT 14 DAYS 
copy summary of theEvent to the end of theTitles
copy stDate to the end of theTimes
end if
end if
end repeat
end tell


-- take the info from ical and make it into a list formatted for reminder, ready to be written to the file
repeat with n from 1 to the number of items in theTimes
set theDate to split(date string of item n of theTimes, space)
-------MY FORMATING IS DOWN BELOW IT CHANGES THE MONTH TO A NUMBER AND GETS RID OF A COMMA AFTER THE PARSE
if the fourth string of theDate is "2009" then
if the second string of theDate = "January" then set the second string of theDate to "1"
if the second string of theDate = "February" then set the second string of theDate to "2"
if the second string of theDate = "March" then set the second string of theDate to "3"
if the second string of theDate = "April" then set the second string of theDate to "4"
if the second string of theDate = "May" then set the second string of theDate to "5"
if the second string of theDate = "June" then set the second string of theDate to "6"
if the second string of theDate = "July" then set the second string of theDate to "7"
if the second string of theDate = "August" then set the second string of theDate to "8"
if the second string of theDate = "September" then set the second string of theDate to "9"
if the second string of theDate = "October" then set the second string of theDate to "10"
if the second string of theDate = "November" then set the second string of theDate to "11"
if the second string of theDate = "December" then set the second string of theDate to "12"
if the third string of theDate = "1," then set the third string of theDate to "1"
if the third string of theDate = "2," then set the third string of theDate to "2"
if the third string of theDate = "3," then set the third string of theDate to "3"
if the third string of theDate = "4," then set the third string of theDate to "4"
if the third string of theDate = "5," then set the third string of theDate to "5"
if the third string of theDate = "6," then set the third string of theDate to "6"
if the third string of theDate = "7," then set the third string of theDate to "7"
if the third string of theDate = "8," then set the third string of theDate to "8"
if the third string of theDate = "9," then set the third string of theDate to "9"
if the third string of theDate = "10," then set the third string of theDate to "10"
if the third string of theDate = "11," then set the third string of theDate to "11"
if the third string of theDate = "12," then set the third string of theDate to "12"
if the third string of theDate = "13," then set the third string of theDate to "13"
if the third string of theDate = "14," then set the third string of theDate to "14"
if the third string of theDate = "15," then set the third string of theDate to "15"
if the third string of theDate = "16," then set the third string of theDate to "16"
if the third string of theDate = "17," then set the third string of theDate to "17"
if the third string of theDate = "18," then set the third string of theDate to "18"
if the third string of theDate = "19," then set the third string of theDate to "19"
if the third string of theDate = "20," then set the third string of theDate to "20"
if the third string of theDate = "21," then set the third string of theDate to "21"
if the third string of theDate = "22," then set the third string of theDate to "22"
if the third string of theDate = "23," then set the third string of theDate to "23"
if the third string of theDate = "24," then set the third string of theDate to "24"
if the third string of theDate = "25," then set the third string of theDate to "25"
if the third string of theDate = "26," then set the third string of theDate to "26"
if the third string of theDate = "27," then set the third string of theDate to "27"
if the third string of theDate = "28," then set the third string of theDate to "28"
if the third string of theDate = "29," then set the third string of theDate to "29"
if the third string of theDate = "30," then set the third string of theDate to "30"
if the third string of theDate = "31," then set the third string of theDate to "31"
-----END OF MY FORMATING
--THESE NEXT TWO IF'S ARE SEEING IF THE HOUR HAS ONE OR TWO NUMBERS AND COPYING THE CORRECT FORMAT TO THE WRITE LIST
if the second string of the time string of item n of theTimes is ":" then
copy the first string of the time string of item n of theTimes & ":" & the third string of the time string of item n of theTimes & the fourth string of the time string of item n of theTimes & " " & the ninth string of the time string of item n of theTimes & the tenth string of the time string of item n of theTimes & "  " & the second string of theDate & "/" & the third string of theDate & " " & item n of theTitles to the end of writeList
end if
if the second string of the time string of item n of theTimes is not ":" then
copy the first string of the time string of item n of theTimes & the second string of the time string of item n of theTimes & ":" & the fourth string of the time string of item n of theTimes & the fifth string of the time string of item n of theTimes & " " & the tenth string of the time string of item n of theTimes & the last string of the time string of item n of theTimes & " " & the second string of theDate & "/" & the third string of theDate & " " & item n of theTitles to the end of writeList
end if

-----copy to a list that will be writen in a format like: 11:00 AM  6/16 Go to airport - Notes(if any)

end if
end repeat



-- Open the file and write the info

try
set foo to (open for access file thePath with write permission)
on error -- need to make this only catch the appropriate error (file already open)
close access thePath
set foo to (open for access file thePath with write permission)
end try

set eof of foo to 0
repeat with theLine in writeList
set theLine to theLine & newline
write (theLine) to foo starting at eof
end repeat
close access foo

-----handler to parse date of the events-----
to split(someText, delimiter)
set AppleScript's text item delimiters to delimiter
set someText to someText's text items
set AppleScript's text item delimiters to {""} --> restore delimiters to default value
return someText
end split
------------Growl notification that gets todays date and the date 2 weeks from now and displays them as "July # - July #" using break to parse the date----------
set list_begdate to {}
set list_enddate to {}
set todaydate to (current date)
set lastdate to (current date) + 14 * days
copy todaydate to list_begdate
copy lastdate to list_enddate
set list_begdate to break(date string of list_begdate, ",")
set list_enddate to break(date string of list_enddate, ",")
set todaydate to the second string of list_begdate
set lastdate to the second string of list_enddate
set dates_added_to_desktop to todaydate & "-" & lastdate as string
-----the break handaler to parse the dates--------
to break(someText, delimiter)
set AppleScript's text item delimiters to delimiter
set someText to someText's text items
set AppleScript's text item delimiters to {""} --> restore delimiters to default value
return someText
end break
------growl notification--------
-------app name to display under in growl prefs----
--------the notiication title---
---------the dates from above to display in description on notification-----
tell application "GrowlHelperApp"
set the allNotificationsList to ¬
{"Done"}
set the enabledNotificationsList to ¬
{"Done"}
register as application ¬
"DeskNote" all notifications allNotificationsList ¬
default notifications enabledNotificationsList ¬
icon of application "iCal"
notify with name ¬
"Done" title ¬
"DeskNotes imported:" description ¬
dates_added_to_desktop application name "DeskNote"
end tell

------I push out the tex file to geektool to display on my desktop and with geektool v3b1 I can script a refresh-----
tell application "GeekTool" to refresh all

Hi, jarstelfox. Welcome to MacScripter and thanks for posting your script.

With regard to its layout, you’ve got bits of the top level code (anything not in a handler) interspersed between the handlers. For legibility, the top level code should be all together at either the beginning or the end of the script.

You’ve used ‘current date’ several times throughout the script. Each time it appears, the function is called and a date is returned. It’s better to use it just once and assign the result to a variable. Reading the date from this variable is more efficient than getting it from the system every time and ensures that the date and time used at the end of the script are the same as at the beginning! The result of ‘time string of item n of theTimes’ could similarly be assigned to a variable, but see my next paragraph.

It’s better not to use ‘date string’, ‘time string’, and ‘short date string’ if you then want to analyse the results. Besides being the least efficient way of getting the information, the analysis only works on computers where the Date and Time preferences are the same as on your own: ie. English month and weekday names, American date order, 12-hour clock, no leading zeros, etc. If you derive the information directly from the date object, the script will work on any computer, even though the end result will be in your own preferred format.

Here’s a go at revamping your script. I don’t have Growl or GeekTool, so I haven’t tested those sections of it.

--------------------------------------------------------------------------------------------------------------
------------------------------------------FAMILY CALENDAR--------------------------------------------------
---------------------------------------------------------------------------------------------------------------
-- Some properties to customise the script.
property calendarName : "Family"
property reminderFolderName : "Remind"
property newline : ASCII character 10 ----for those who don't know ascii character 10 is the enter/return button-----

----------sort function: get start dates and summaries from iCal events and sort them by start date. Should be faster than sorting the events themselves---------
on sortEvents(eventList)
	set theDates to {}
	set theSummaries to {}
	
	set eventCount to (count eventList)
	if (eventCount > 0) then
		-- Get the start dates and summaries from the iCal events, simultaneously ordering them by start date using an insertion sort.
		tell application "iCal" to set {start date:lastDate, summary:end of theSummaries} to beginning of eventList
		set end of theDates to lastDate
		repeat with i from 2 to eventCount
			tell application "iCal" to set {start date:thisDate, summary:thisSummary} to item i of eventList
			set {end of theDates, end of theSummaries} to {thisDate, thisSummary}
			if (thisDate comes before lastDate) then
				repeat with j from i to 2 by -1
					tell item (j - 1) of theDates
						if (thisDate comes before it) then
							set item j of theDates to it
							set item j of theSummaries to item (j - 1) of theSummaries
						else
							set item j of theDates to thisDate
							set item j of theSummaries to thisSummary
							set j to 0
							exit repeat
						end if
					end tell
				end repeat
				if (j is 2) then
					set item 1 of theDates to thisDate
					set item 1 of theSummaries to thisSummary
				end if
			else
				set lastDate to thisDate
			end if
		end repeat
	end if
	
	return {theDates, theSummaries}
end sortEvents
-------------------------------------------
-- Construct a line for the reminder file.
to formatEntry(theDate, theSummary)
	set {month:m, day:d, time:t} to theDate
	if (t < 12 * hours) then
		set x to " AM"
	else
		set x to " PM"
	end if
	tell (((t div hours + 11) mod 12 + 1) * 100 + t mod hours div minutes) as text
		return text 1 thru -3 & ":" & text -2 thru -1 & x & " " & (m as integer) & "/" & d & " " & theSummary
	end tell
end formatEntry

-- Find out if iCal is open so we know whether or not to close it later.
tell application "System Events" ----open iCal
	if (exists application process "iCal") then
		set iCalOpen to true
	end if
end tell

set now to (current date)
set twoWeeksTime to now + 2 * weeks
tell application "iCal"
	set eventList to every event of calendar calendarName whose (start date comes after now) and (start date comes before twoWeeksTime)
	set {theTimes, theTitles} to my sortEvents(eventList)
end tell

set writeList to {} ----what I will write to the text file
repeat with i from 1 to (count theTimes)
	set stDate to item i of theTimes
	set thisSummary to item i of theTitles
	set the end of writeList to my formatEntry(stDate, thisSummary)
end repeat
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to newline
set writeList to writeList as text
set AppleScript's text item delimiters to astid

set reminderFolder to (path to "docs" as Unicode text) & reminderFolderName & ":"
set thePath to reminderFolder & calendarName & ".txt" -- Path to a file named after the calendar .
-- Open the file and write the info
set foo to (open for access file thePath with write permission)
try
	set eof foo to 0
	write writeList as string to foo
end try
close access foo

------------Growl notification that gets todays date and the date 2 weeks from now and displays them as "July # - July #" using break to parse the date----------
-- The month(s) will be in English.
set dates_added_to_desktop to (now's month as text) & " " & now's day & " - " & twoWeeksTime's month & " " & twoWeeksTime's day

------growl notification--------
-------app name to display under in growl prefs----
--------the notiication title---
---------the dates from above to display in description on notification-----
tell application "GrowlHelperApp"
	set the allNotificationsList to ¬
		{"Done"}
	set the enabledNotificationsList to ¬
		{"Done"}
	register as application ¬
		"DeskNote" all notifications allNotificationsList ¬
		default notifications enabledNotificationsList ¬
		icon of application "iCal"
	notify with name ¬
		"Done" title ¬
		"DeskNotes imported:" description ¬
		dates_added_to_desktop application name "DeskNote"
end tell

------I push out the text file to geektool to display on my desktop and with geektool v3b1 I can script a refresh-----
tell application "GeekTool" to refresh all

Edits: Corrected a couple of my own errors. Streamlined the sortEvents() handler.

wow, this works great! I timed my original script at about 30 sec. and this one at about 5 sec. I am looking to add some more features but have no clue, if u have any ideas let me know. :smiley: