AppleScripting based on certain patterns?

Hello everybody,

Last time I posted in this forum you were all extremely helpful, so I was hoping you might be willing to help me on another scripting issue…

I’m provided a .txt file which contains my roster for my working week as it is variable, and it also contains everybody else so what I’m trying to do is have it extract each individual person’s roster, and if it matches my name, creates iCal events based on the times it provides.

Sounds simple enough, but there’s lots of snags in the process as you might notice; Here is a copy of my section of my roster below (Everybody’s looks the same):

A chunk of the roster is visible at this URL

The entire roster is full of spaces and tabs rather than actual columns, the page number is not always 38 and there’s always the white space prior to my employee number but the amount of it is random each week.

The only pattern that is always the same is that if all the whitespace is removed, it has this for the shifts:

Ideally I would have it strip out everything except the number, my name, and the format mentioned above, and have a variable/list with each day’s chunk separate to make it easier to put them into a calendar.

The script I have so far feels really clunky and a really dodgy method of doing it, but my lack of experience is holding me down with fixing it.

If anybody was willing to help out with this I would be very grateful.

Here’s the script I have so far (It’s not finished, but so far it’s pretty sucky):


	The functions .

on splitText(delimiter, someText)
	set prevTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to delimiter
	set output to text items of someText
	set AppleScript's text item delimiters to prevTIDs
	return output
end splitText
-- Example
-- splitText(",", "a,b,c,d,e")

on findAndReplace(tofind, toreplace, TheString)
	set ditd to text item delimiters
	set text item delimiters to tofind
	set textItems to text items of TheString
	set text item delimiters to toreplace
	if (class of TheString is string) then
		set res to textItems as string
	else -- if (class of TheString is Unicode text) then
		set res to textItems as Unicode text
	end if
	set text item delimiters to ditd
	return res
end findAndReplace




-- Set the variables
set userName to "24367 Matthew Stares "
set endRosterChunk to "____________________"
set rosterFolder to "Macintosh HD:Users:Mat:Dropbox:Lunch Rosters:"
-- Set the roster location, this is temporary until it searches for all
set theRoster to "Macintosh HD:Users:Mat:Dropbox:Lunch Rosters: P12W02.txt" as alias

open for access theRoster
set theData to read theRoster
close access theRoster

--Split each roster into 'chunks' of individual user's rosters
set theDataSplit to splitText(endRosterChunk, theData)
set totalAdvisors to count of theDataSplit

--Get the week it runs for
set startDate to text 86 through 93 of (item 1 of theDataSplit)
set splitStartDate to splitText("/", startDate)
-- Convert it to an Australian format for calendar usage
set theStartWeek to (item 2 of splitStartDate) & "/" & (item 1 of splitStartDate) & "/" & (item 3 of splitStartDate)

--The formats are as follows:
--ID - XXXXX (5 Numbers)
--Date - mm/dd/yy
--Day - Sun/Mon/etc.
--Start/Stop - hh:mm
--Exception Code - Break/Lunch/Vacation/Public Holiday

repeat with i from 1 to count of theDataSplit
	set tempString to item i of theDataSplit as text
	
	-- remove spaces for easier formatting (It won't work for new lines though..)
	set tempString to findAndReplace("	", "", tempString)
	set item i of theDataSplit to findAndReplace(" ", "", tempString)
end repeat


-- Testing breaking it down into parts based on newlines
set tempString to item 38 of theDataSplit
set AppleScript's text item delimiters to return
set testTempString to text items of tempString

(*
	Output of the above little testing chunk:

{"", "
", "
IEXTotalViewPage:38", "
Date:03/17/13to03/23/13WeeklySchedules", "
Thursday,03/14/13", "
", "
ManagementUnit:T2", "
", "
", "
Sortedby:Name", "
ReportAcrossAgentMoves:NoReportAgentMoves:No", "
", "
", "
24367MatthewStares", "
DateDayStartStopExceptionCodeStartStop", "
______________________________________________________", "
03/17/13SunOFFOFF", "
03/18/13Mon08:0017:00Break10:2010:30", "
Lunch13:0014:00", "
Break15:3515:45", "
03/19/13Tue08:0016:30Break10:0510:15", "
Lunch12:3013:30", "
Break15:2015:30", "
03/20/13Wed08:0016:30Break09:3509:45", "
Lunch11:3012:30", "
Break14:3014:40", "
03/21/13ThuOFFOFF", "
03/22/13Fri08:0016:30Break10:2010:30", "
Lunch12:3013:30", "
Break15:0515:15", "
03/23/13Sat08:0016:30Break10:2010:30", "
Lunch12:3013:30", "
Break15:0515:15", "
", "
"}

*)

--Trying to get only the parts from my name until the end of the week.
set onetestTempString to items 14 through -2 of testTempString

(*
	Output after running the bit above:

{"
24367MatthewStares", "
DateDayStartStopExceptionCodeStartStop", "
______________________________________________________", "
03/17/13SunOFFOFF", "
03/18/13Mon08:0017:00Break10:2010:30", "
Lunch13:0014:00", "
Break15:3515:45", "
03/19/13Tue08:0016:30Break10:0510:15", "
Lunch12:3013:30", "
Break15:2015:30", "
03/20/13Wed08:0016:30Break09:3509:45", "
Lunch11:3012:30", "
Break14:3014:40", "
03/21/13ThuOFFOFF", "
03/22/13Fri08:0016:30Break10:2010:30", "
Lunch12:3013:30", "
Break15:0515:15", "
03/23/13Sat08:0016:30Break10:2010:30", "
Lunch12:3013:30", "
Break15:0515:15", "
"}

)*

Model: Mac Mini
Browser: Chrome
Operating System: Mac OS X (10.8)

Hi.

You don’t need everybody’s rosters, do you? If your employee number and name are guaranteed to be inset from the beginning of their line, you can extract your own roster like this:

-- Set the variables
set userName to "24367 Matthew Stares "
set endRosterChunk to "____________________"
set rosterFolder to "Macintosh HD:Users:Mat:Dropbox:Lunch Rosters:"
-- Set the roster location, this is temporary until it searches for all
set theRoster to "Macintosh HD:Users:Mat:Dropbox:Lunch Rosters: P12W02.txt" as alias

set theData to (read theRoster)

-- Isolate the section of data containing "my" roster.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to userName
set theData to text from paragraph (count paragraphs of text item 1 of theData) to end of theData
set AppleScript's text item delimiters to endRosterChunk
set myRoster to text item 1 of theData
set AppleScript's text item delimiters to astid

myRoster

After that, I’m not clear if you just want events for your shifts or for the breaks as well.

Thankyou VERY much! That’s a far easier method of doing it.

The next step is to extract each individual day and each of the breaks/lunch/“OFF” as they’re all going to be imported into iCal as events.

Hello

See what you get if you can get the paragraphs of the text, and loop over each paragraph, making an event out of each.

Right. For each shift and break event you’ll need a summary and the start and end dates with times. For an “OFF” or “Vacation” event, you’ll need the summary and start and end dates with their times set to a suitable offset to get the ‘allday event’ property to work properly. Setting the times to one second after midnight works for your mid-March dates in the GMT time zone, but adjustments may be required at your end.

I’ve stuck to AppleScript methods as there’s no particular advantage here to using “sed” or something similar. I’ve also assumed no night shifts going past midnight!

Presumably the idea is to look at the events in day or week view, where their duration and overlap are shown graphically.

roster2iCal()

on roster2iCal()
	-- Set the variables
	set userName to "24367 Matthew Stares "
	set endRosterChunk to "____________________"
	set rosterFolder to "Macintosh HD:Users:Mat:Dropbox:Lunch Rosters:"
	-- Set the roster location, this is temporary until it searches for all
	set theRoster to "Macintosh HD:Users:Mat:Dropbox:Lunch Rosters: P12W02.txt" as alias
	
	set theData to (read theRoster)
	
	-- Isolate the section of text containing "my" roster.
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to userName
	set theData to text from paragraph (count paragraphs of text item 1 of theData) to end of theData
	set AppleScript's text item delimiters to endRosterChunk
	set myRoster to paragraphs of text item 1 of theData
	set AppleScript's text item delimiters to astid
	
	tell application "iCal" to activate
	
	set startDate to (current date) -- Create a date object whose properties we'll set below.
	set onVacation to false -- For convenience, start each rota week not already being on holiday!
	repeat with thisLine in myRoster
		if (thisLine contains "Vacation") then -- This line indicates a vacation day.
			-- If already on vacation (this week), just advance the current end date.
			-- Otherwise note the vacation start date and set the onVacation flag.
			if (onVacation) then
				set endDate to endDate + days
			else
				tell startDate to set {day, time, year, {its month, day}} to {1, 1, 2000 + (word 3 of thisLine), words 1 thru 2 of thisLine}
				copy startDate to endDate
				set onVacation to true
			end if
		else -- This line doesn't indicate a vacation day.
			-- If it's the day after a vacation, make an all-day event for this week's portion of the entire vacation and unset the onVacation flag.
			if (onVacation) then
				makeEvent for "Vacation" from startDate to endDate between "00:01 00:01" with allday
				set onVacation to false
			end if
			if ((count thisLine's contents's words) > 0) then
				-- If thisLine's not blank, it contains either a date and "OFF", a date and shift and break details, or just break details.
				if (thisLine contains "/") then -- It begins with a date.
					-- Initialise start and end dates to the date.
					tell startDate to set {day, year, {its month, day}} to {1, 2000 + (word 3 of thisLine), words 1 thru 2 of thisLine}
					copy startDate to endDate
					-- If the line contains "OFF", make an all-day event for it.
					-- Otherwise make timed events for both the shift and the break.
					if (thisLine contains "OFF") then
						makeEvent for "Off" from startDate to endDate between "00:01 00:01" with allday
					else
						makeEvent for "On" from startDate to endDate between (text from word 5 to word 8 of thisLine) without allday
						makeEvent for (word 9 of thisLine) from startDate to endDate between (text from word 10 to word 13 of thisLine) without allday
					end if
				else -- It's a line containing only break details.
					-- Make a timed event for the break.
					makeEvent for (word 1 of thisLine) from startDate to endDate between (text from word 2 to word 5 of thisLine) without allday
				end if
			end if
		end if
	end repeat
end roster2iCal

on makeEvent for theSummary from startDate to endDate between theseTimes given allday:allday
	tell startDate to set {its hours, its minutes} to words 1 thru 2 of theseTimes
	tell endDate to set {its hours, its minutes} to words 3 thru 4 of theseTimes
	tell application "iCal" to make new event at end of events of calendar "Work" with properties {summary:theSummary, start date:startDate, end date:endDate, allday event:allday}
end makeEvent

You are a legend :slight_smile: I can’t thank you enough for all the help, though I’ve just been testing this script out with the rosters I have and there’s a few problems…

This section throws an error saying endDate is not set:

	else -- It's a line containing only break details.
					-- Make a timed event for the break.
					makeEvent for (word 1 of thisLine) from startDate to endDate between (text from word 2 to word 5 of thisLine) without allday
				end if

I’ve tried to understand the entire script to work out why, but just don’t see why… The rest of it works wonderfully that I’ve tested though.

Also, is there an easy way for it to detect if duplicated events exist for it? For instance, currently if the script runs three times the calendar is full of those shifts duplicated.

EDIT: I ended up finding why it failed, it was trying to do it for the first two lines, so I changed it to this:

if (thisLine does not contain userName) then
						if (thisLine does not start with "Date") then
							makeEvent for (word 1 of thisLine) from startDate to endDate between (text from word 2 to word 5 of thisLine) without allday
						end if
					end if