AppleScript iCal Scheduler

This should be setup as a Application, and when run, this will prompt you for some various bits of information.
I have written a TON of scripts, and this one I think is very polished, and works very well


--Important Note:  All times must be entered in military time, so 3:00PM is "15"
-- Added support for short hours (different length of shift) to enter these, you would do "13-1" This would start the shift at 1pm, and with shift_length being 9 hours, would reduce this by 1 hour, so a 1-9 would be scheduled, instead of a 1-10

property shift_length : "9" --Number of hours shift last, defaults to 9, 8 hours + 1 hour lunch
property shift_start : "8" --Default shift start time, good thing to set if you always start at a certain time.  Once you enter another entry in the field, it will default to that time.
property calendar_name : "" --The Calendar you want to save it to in iCal
property store_name : "" --Default "Location" field
property vernum : " iEnter 2.0.2d" --Version Tracking information --if there is a "d" its dev version which has comments
property Event_name : "Work" --iCal event name 
property iEnterOutPutFile : "iEnterDataFile.txt"
property ChangeEndTimeOffset : 0
(*
Version History: 
 
1.0
This version works, but needs some shiny things
1.2
Added Icon when adding schedule
1.3 
Added Dummy logic to entered time.
-If user enters anything aside from 1-24, it will present a message, and only option is to quit.
1.5
Aded Logic to ask name of location and calendar name.
1.5.1
Added Variable so you can change what the event name is.
1.5.2
Pulled list of available calendars from ical, so one less thing a user can screw up.
Changed bounds checking to allow entry as 8.5 for a 8:30 start, etc
Published Calendar of release schedule.
1.5.5
Added config file to save data, so when users change versions of iEnter, they dont need to resetup everything
Added Option to allow you to enter number of hours in workday.
Showed Calendar that is being saved to in data entry dialog
1.5.6
Showing event name and location in data entry dialog
1.6
Fixed issues with application not opening iCal (Calendar)
Changed all references to "iCal" or "Calendar" to.
tell application id "com.apple.iCal"
Added support for short hours (different length of shift) to enter these, you would do "13-1" This would start the shift at 1pm, and with shift_length being 9 hours, would reduce this by 1 hour, so a 1-9 would be scheduled, instead of a 1-10
2.0
Awesome new feature, now checks to see what your last scheduled day was, and then asks you about the next day, no longer have to click "Day off" until you catch up.
2.0.1
Marks the event_name with a "¢" in the beginning if the offset (ex 13-2 in the time line)
d
All future version will have a "d" on it, as the script is now readable in applescript editor and i have included comments
*)

on run {}
	--checks to see if ical is running, if it isnt, then it launches it
	tell application "System Events"
		if (count of (every process whose bundle identifier is "com.apple.iCal")) = 0 then
			tell application id "com.apple.iCal" to activate -- Used bundle instead of application as iCal is now Calendaar.  It "may" work that way, but i can see the user clicking on the app, and getting the dreaded applescript "Which Application are you talking about"  Bad experience i guess
		end if
	end tell
	--
	--checks exists on a file on the filesystem.  if it finds the file, the script assumes its been "setup" and skips running setup
	tell application "Finder"
		if exists file ((path to documents folder) & iEnterOutPutFile as string) then
			set RunSetup to false
		else
			set RunSetup to true
		end if
	end tell
	--
	
	-- THIS IS setup
	if RunSetup = true then
		--If iCal is running, pull a list of calendars
		tell application id "com.apple.iCal" to set theCalendars to name of every calendar
		
		--this used to ask the user which calendar, and they would type it in.  i used an error block in case the dumb user put in something like "peanut butter".  This will now just list the calendars, and the user chooses one. 
		try
			set calendar_name to item 1 of (choose from list theCalendars with prompt "Choose the name of the Calendar you would like to Sync to: " OK button name "Continue" cancel button name "Quit")
		on error
			--dumped us out on error
			return false
			
		end try
		--
		-- Ask the user for the correct data
		set store_name to text returned of (display dialog "Enter the name of the Location" default answer store_name)
		set Event_name to text returned of (display dialog "Enter the name of the Event" default answer Event_name)
		set shift_length to text returned of (display dialog "How long are the shifts?" default answer shift_length)
		--
		--this will actually write the data to the file in the filesystem
		my save_data()
		--
		--Ahh yess, this, well, could be done I suppose but havent yet, maybe in 2.0 ?
		--set appwalkthrough to button returned of (display dialog "Would you like an tutorial on how to use this application?" buttons {"No", "Yes"} default button "Yes")
		--if appwalkthrough is "Yes" then
		--display dialog "When the app launches, it will start on todays date.  If you've previously used the app it may just bump you to the next day where you dont have an event with the name as specified listed. -- run example of there data, and maybe a "simulator"
		--end if
		
	end if
	--
	
	--This will fire off a subroutine to actually read the data from the text file on the filesystem.  not much error checking here, but any error would just exit the app, so its psuedo safe
	my read_data()
	--
	
	--new block, starts you where you havent schduled a shift start yet
	tell application id "com.apple.iCal"
		tell calendar calendar_name
			-- asks ical to look for your scheduled event in the future, and return a list
			set theEventList to every event whose summary contains Event_name and start date > (current date)
			--set theEventList to every event whose summary contains Event_name and start date > date string of (current date)
		end tell
		--
		-- pulls current date
		set thetest to current date
		--
		--searches list of events "theEventList", and finds the oldest one.  I know "every" is suppose to return them in order, but it doesnt, otherwise i could pull a last item of theEventList and call it a day
		repeat with i from 1 to length of theEventList
			if (start date of item i of theEventList) > thetest then
				set thetest to (start date of item i of theEventList)
			end if
		end repeat
		--thetest will end up as the oldest date in the list, so we use that, this compares thetest to now, which returns a second offset, we devide that by number of seconds in the day.  this is actually a bug as sometimes when the user is prompted to enter his next scheduled day, it may be off by 1, im sure there is an easy way to fix that either rounding of user "days" instead of 86400 but shrug --fixed issue i was using "div"operator which chops off tbe decimal, so it was returning 2.917237 but div returns 2, now i just straight devide and round to the nearest whole number
		
		set schduledOffset to round (thetest - (current date)) / 86400
	end tell
	--
	
	--Will front this application once it prompts the user
	tell me to activate
	--
	
	--loop starts interaction with user about the next scheduled event, im some case, this may be the first time the user sees us =)
	repeat with i from (schduledOffset + 1) to 60
		
		set ChangeEndTimeOffset to 0
		set userdata to (display dialog "Enter Start Time for:
" & date string of ((current date) + i * days) & "
Calendar: " & calendar_name & "
Event: " & Event_name & " @ " & store_name default answer shift_start buttons {"Quit", "Day Off", "Next Day"} default button 3 cancel button 1 with title vernum with icon path to resource "App.icns" in bundle (path to me))
		
		--if quit is chosen, this fires, which will exit the app
		if button returned of userdata is "Quit" then
			exit repeat
		end if
		--
		
		--new block, adds short shift support
		--looks at he string the user enters, for "-" charecter, if it finds one, it spilts the string using stringtolist which returns a list of strings using "-" as the delimter
		--grabs the first item, and pushes it to user returned data, as you can see this wasnt added right away, but this seems like a solution.  the second item is our offset we need
		if (text returned of userdata as string) contains "-" then
			set ChangeEndTime to stringtolist((text returned of userdata), "-")
			set text returned of userdata to (item 1 of ChangeEndTime)
			set ChangeEndTimeOffset to (item 2 of ChangeEndTime)
		end if
		
		
		try
			--Makes sure the user did not enter alpha characters .  This coercern will fail, and trigger the on error block which alerts the user, and exits repeat, which in turn quits the app
			set checkdata to (text returned of userdata as number)
			--if an error occures we need to error the repeat to force the app to close
		on error errnum
			display dialog "All times must be entered in military time, so 3:00PM is \"15\"
" & errnum with icon stop buttons "Quit" default button 1
			exit repeat
		end try
		--if we get here, no error occured and we move on
		--so we know the data recieved as at least valid.  next block will also check to make sure the numbers are under 24
		--all this is done to make sure the data that gets recorded in ical is as accurate as possible, so we run this bounds checks to make sure no error occurs
		if (text returned of userdata as number) ≤ 24 then
			if button returned of userdata is "Next Day" then
				set shift_start to text returned of userdata
				my create_event(date string of ((current date) + i * days) as string, text returned of userdata)
			end if
			if button returned of userdata is "Day Off" then --a bit redundent i am sure, just wanted to add it in case i wanted to fire something at some point
			end if
		else
			display dialog "Only numbers 0-24 are valid" with icon stop buttons "Quit" default button 1
			exit repeat
		end if
	end repeat
end run

-- subroutine for .creating events 
--So in theory this wouldn't be called if we didnt have all the data
on create_event(theDate, startime)
	set adjusteddata to ((date (theDate as string)) + startime * hours)
	
	--check to see if the offset option is used (using a minus in text field) and adds an "*" to the beginning of the Event_name.  again, kind of lazy but i didnt want to break Event_name value
	tell application id "com.apple.iCal"
		tell calendar calendar_name
			--if ChangeEndTimeOffset wasnt changed, it does a "standard" calendar write.. oh yes, this is "middle" and the point to all of this =)
			-- when testing the script, i will often just comment out these 2 lines to test.
			if ChangeEndTimeOffset is 0 then
				make new event at end with properties {summary:Event_name, location:store_name, start date:adjusteddata, end date:adjusteddata + shift_length * hours - ChangeEndTimeOffset * hours}
			else
				make new event at end with properties {summary:"¢" & Event_name, location:store_name, start date:adjusteddata, end date:adjusteddata + shift_length * hours - ChangeEndTimeOffset * hours}
			end if
		end tell
	end tell
end create_event
--takes the variable data and saves it to the filesystem
on save_data()
	set ref_num to open for access file ((path to documents folder) & iEnterOutPutFile as string) with write permission
	set eof of ref_num to 0
	write calendar_name & return & store_name & return & Event_name & return & shift_length to ref_num
	close access ref_num
end save_data
--takes the the data in the filesystem, and writes to to a variable
on read_data()
	set ienterdatapath to ((path to documents folder) & iEnterOutPutFile as string)
	set ref_num to open for access file ienterdatapath with write permission
	set ienterdata to read ref_num
	close access ref_num
	set {calendar_name, store_name, Event_name, shift_length} to stringtolist(ienterdata, return)
end read_data

--takes a string of data like "hello, my name is Mike" and converts it into {"hello,","my","name","is","Mike"} so that you can specify first item, second item or walk down a list searching, listtostring() does the opposite.  listtostring is not used in this script, but I have included that code below, as these 2 should always be with each other.
on stringtolist(theString, delim)
	set oldelim to AppleScript's text item delimiters
	set AppleScript's text item delimiters to delim
	set dlist to (every text item of theString)
	set AppleScript's text item delimiters to oldelim
	return dlist
end stringtolist
on ListToString(theList, delim)
	set oldelim to AppleScript's text item delimiters
	set AppleScript's text item delimiters to delim
	set alist to theList as string
	set AppleScript's text item delimiters to oldelim
	return alist
end ListToString