Need help with Excel to iCal To-Do...

I am so glad I found MacScripter.Net

I have searched through this forum and found a few threads by Bluenote and others that cover very similar to my needs. However, I am getting stuck trying to utilise those portions of scripts.

I need get each row of a daily Excel file into an existing iCal ‘DailyImport’ calendar as separate To Do items.
The boss will then drag (schedule) each task to each of the other calendars.

Each daily Excel file does contain jobs that are new and jobs that are existing, so if any matching jobs are already in the To Do Items, then they should not get added, only the new (non-mathing) ones.

The Excel file is simple:

Due Job Title Qty Customer Salesperson
20/01/12 15001 Test Job Title One 2,000 Client One John Smith
13/01/12 15002 Test Job Title Two 15,000 Client Two Mary Smith
etc.

The resulting To Do item should be processed out of Excel to look like this:

Title of To Do Item: 15001 ¢ Test Job Title One ¢ 2,000 ¢ Client One ¢ John Smith
Scheduled for: 20 January 2012

I would be extremely grateful if someone can help.

Also, I will be calling this script with Automator, to run on a dropped Excel file in a watched folder.

Thanks in advance…

David.

What do you have so far?

Embarassingly, very little.
Nothing worth posting, except I have tried to start with what I found here:
http://macscripter.net/viewtopic.php?id=21232

Also, the daily Excel file that is sent to me, usually has only about 20-30 rows, and 10 or so of those will be new jobs.

Hi,

try this


property calendarName : "Job"

tell application "Microsoft Excel"
	set usedRange to used range of active sheet
	set {ASTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, " ¢ "}
	
	repeat with i from 2 to (count rows of usedRange)
		set valueList to (value of row i of usedRange)
		set valueList to item 1 of valueList
		set {theDate, theJob} to items 1 thru 2 of valueList
		set item 2 of valueList to theJob as integer
		set theTitle to (items 2 thru -1 of valueList) as text
		my createToDo(theDate, theTitle)
		
	end repeat
	set AppleScript's text item delimiters to ASTID
end tell

on createToDo(dueDate, theSummary)
	tell application "iCal"
		tell calendar calendarName
			make new todo at end of todos with properties {due date:dueDate, summary:theSummary}
		end tell
	end tell
end createToDo

Wow Stefan. I was looking at it all wrong.
Your script works, the To Do items get created, but there are two issues that I don’t know how to fix.

  1. Applescript throws an error, highlighting the code:

make new todo at end of todos with properties {due date:dueDate, summary:theSummary}

the error being:

error “iCal got an error: Can’t make "" into type date.” number -1700 from “” to date

  1. The script re-adds matching items.

I really appreciate this help you are giving me.

David.

  1. I assumed that the cells in the “Due” column are formatted as date. If the format is text, try

make new todo at end of todos with properties {due date:date dueDate, summary:theSummary}

  1. the script creates todos from the Excel sheet, nothing else. There is no further content check

That gets the following error:
error “iCal got an error: Can’t get date (date "Thursday, 26 April 2012 12:00:00 AM") of calendar "Job".” number -1728 from date (date “Thursday, 26 April 2012 12:00:00 AM”) of calendar “Job”

The cells were formatted as ‘date’ (dd/mm/yyyy). The year is 4 digit, but displayed as two.

Also noticed the formatting for the quantity is screwy. Instead of “30,000” it comes out as “3.0E+4”, example:
15003 ¢ Test Job Title Three ¢ 3.0E+4 ¢ Client Three ¢ Paul Smith

Test Excel file is:
Due Job Title Qty Customer Salesperson
26/04/12 15001 Test Job Title One 2,000 Client One John Smith
13/05/12 15002 Test Job Title Two 15,000 Client Two Mary Smith
15/05/12 15003 Test Job Title Three 30,000 Client Three Paul Smith

OK, I misinterpreted the error.
The column is indeed formatted as date, so delete the date specifier in the make new todo line
The error occurred when the cell was empty. What should happen in this case?

Is the comma in 2,000 the decimal separator? If yes, is the integer portion sufficient?

Full circle then as this code gives that same original error:
make new todo at end of todos with properties {due date:dueDate, summary:theSummary}

No its not a decimal, just indicates the thousands. Unfortunately I have no say on the formatting of the Excel file.

the appearance of the number format depends on the date format settings in the international pref pane.
Here in Switzerland the comma is the decimal separator.

This version of the script reads the date(s) separately and considers all other values as string values,
the formatting should be displayed correctly now

The date error occurs when the content of the current date cell is empty or no valid date


property calendarName : "Job"

tell application "Microsoft Excel"
	set usedRange to used range of active sheet
	set {ASTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, " ¢ "}
	
	repeat with i from 2 to (count rows of usedRange)
		set theDate to value of cell 1 of row i of usedRange
		set valueList to (string value of row i of usedRange)
		set valueList to item 1 of valueList
		set theTitle to (items 2 thru -1 of valueList) as text
		my createToDo(theDate, theTitle)
		
	end repeat
	set AppleScript's text item delimiters to ASTID
end tell

on createToDo(dueDate, theSummary)
	tell application "iCal"
		tell calendar calendarName
			make new todo at end of todos with properties {due date:dueDate, summary:theSummary}
		end tell
	end tell
end createToDo


Thanks Stefan, that fixed the quantity formatting issue.

All the items get correctly added to the To Do list, but ends with the same error, regardless of what date format I set in Excel:

Seems to be because there is no more rows to process out of Excel.

Just need a way to stop the script at the end of the rows.

The script filters the used range of the Excel sheet, probably there are used cells below the last due date.
Insert the last line of this snippet, it exits the loop in case of an empty cell


.
repeat with i from 2 to (count rows of usedRange)
	set theDate to value of cell 1 of row i of usedRange
	if class of theDate is not date then exit repeat
.

Got it. I just discovered the Replies option in AppleScript Editor.
I am very grateful, Stefan.
AppleScript certainly opens up a universe of possibilities

I am going to try to figure out matching the rows and excluding duplicates.


property calendarName : "DailyImport"

tell application "Microsoft Excel"
	set usedRange to used range of active sheet
	set {ASTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, " ¢ "}
	
	repeat with i from 2 to (count rows of usedRange)
		set theDate to value of cell 1 of row i of usedRange
		if class of theDate is not date then exit repeat
		set valueList to (string value of row i of usedRange)
		set valueList to item 1 of valueList
		set theTitle to (items 2 thru -1 of valueList) as text
		my createToDo(theDate, theTitle)
		
	end repeat
	set AppleScript's text item delimiters to ASTID
end tell

on createToDo(dueDate, theSummary)
	tell application "iCal"
		tell calendar calendarName
			make new todo at end of todos with properties {due date:dueDate, summary:theSummary}
		end tell
	end tell
end createToDo

Although working with a few lists in AS is fine, when I need to deal with a large data set I import it into sqlite3 (already installed) first. Manipulating data is much easier in a database.

I had originally intended to use Filemaker, but discovered that iCal and AppleScript had all that’s needed for a task like this. Just I had no idea about running AppleScripts… but keen to learn.

I found a script, for removing duplicate iCal events, but the dev seems to have disappeared:
http://www.the-word-is-not-enough.com/dedupe.php

I want to edit this to remove duplicate To Do’s, not Events.

Can I just change references for “events” to “todo” in the following?


tell application "iCal"
	--	set sourceCal to first calendar whose title is (item 1 of (choose from list (title of (every calendar) as list) with prompt "Choose calendar to delete duplicates"))
	set theCalendars to every calendar
	set testThen to (current date) -- time tester
	
end tell
set n to 0
repeat with sourceCal in theCalendars
	tell application "iCal"
		set mySumms to summary of events of sourceCal
		set myStarts to start date of events of sourceCal
		set myEnds to end date of events of sourceCal
		set myUIDs to uid of events of sourceCal
	end tell
	
	script myStuff
		property aSumms : {}
		property aStarts : {}
		property aEnds : {}
		property aUIDs : {}
		property aDeletes : {}
	end script
	
	set myStuff's aSumms to mySumms
	set myStuff's aStarts to myStarts
	set myStuff's aUIDs to myUIDs
	set myStuff's aEnds to myEnds
	set myLength to length of myStuff's aStarts
	
	repeat with aNum from 1 to (myLength - 1)
		set thisSumm to (item aNum of myStuff's aSumms)
		set thisStart to (item aNum of myStuff's aStarts)
		set thisEnd to (item aNum of myStuff's aEnds)
		repeat with bNum from (aNum + 1) to myLength
			set thatSumm to (item bNum of myStuff's aSumms)
			set thatStart to (item bNum of myStuff's aStarts)
			set thatEnd to (item bNum of myStuff's aEnds)
			if thisSumm is equal to thatSumm and thisStart is equal to thatStart and thisEnd is equal to thatEnd then
				set the end of myStuff's aDeletes to (item bNum of myStuff's aUIDs)
				exit repeat
			end if
		end repeat
	end repeat
	
	set n to n + (count of myStuff's aDeletes)
	
	tell application "iCal"
		repeat with myDel in myStuff's aDeletes
			delete (every event of sourceCal whose uid is myDel)
		end repeat
	end tell
end repeat
tell application "iCal"
	display dialog (n & " duplicates deleted in " & ((current date) - testThen) & " seconds") as text -- time tester=20
end tell

try this


property calendarName : "DailyImport"

tell application "iCal" to set allTodos to every todo of calendar calendarName
set duplicates to findDuplicates(allTodos)
repeat with anItem in duplicates
	tell application "iCal" to delete anItem
end repeat

on findDuplicates(x)
	set origs to {}
	set dels to {}
	
	script a
		property theList : x
		property originals : origs
		property itemsToDelete : dels
	end script
	
	repeat with anItem in a's theList
		if elementIsInList(anItem, origs) then
			set a's itemsToDelete's end to contents of anItem
		else
			set a's originals's end to contents of anItem
		end if
	end repeat
	
	return dels
end findDuplicates

-- two todos are equal if both due dates and summaries are equal 
on elementIsInList(anElement, theList)
	using terms from application "iCal"
		repeat with anItem in theList
			if (due date of anItem is equal to due date of anElement) and (summary of anItem is equal to summary of anElement) then
				return true
			end if
		end repeat
		return false
	end using terms from
end elementIsInList


:slight_smile: You are a guru. :slight_smile:
Your scripting is pure magic.
I am very thankful.
God bless.

David.

Hi All,

Really cool stuff here, that saved me some time. I have merged the bits a bit and updated it to the Reminders App in 10.11.

Feel free to use it as you see fit. (My Excel columns are: Title, Priority(3-0), Person(not used in script), Due Date, Status, Notes)


property todoAccount : "iCloud"
property todoList : "Work"
property doCleanup : true


tell application "Microsoft Excel"
	set usedRange to used range of active sheet
	set {ASTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, " ¢ "}
	
	repeat with i from 2 to (count rows of usedRange)
		
		set theDate to value of cell 4 of row i of usedRange --as date
		if class of theDate is not date then exit repeat
		set theStatus to value of cell 4 of row i of usedRange
		if theStatus is not "Done" then
			set thePriority to value of cell 2 of row i of usedRange
			set theName to value of cell 1 of row i of usedRange
			set valueList to (string value of row i of usedRange)
			set valueList to item 1 of valueList
			set theTitle to (items 2 thru -1 of valueList) as text
			
			my createToDo(theDate, theTitle, thePriority, theName)
		end if
	end repeat
	my doTheCleanup()
	set AppleScript's text item delimiters to ASTID
end tell


on doTheCleanup()
	tell application "Reminders" to set allTodos to every reminder of list todoList of account todoAccount
	set duplicates to findDuplicates(allTodos)
	repeat with anItem in duplicates
		tell application "Reminders" to delete anItem
	end repeat
end doTheCleanup

on findDuplicates(x)
	set origs to {}
	set dels to {}
	
	script a
		property theList : x
		property originals : origs
		property itemsToDelete : dels
	end script
	
	repeat with anItem in a's theList
		if elementIsInList(anItem, origs) then
			set a's itemsToDelete's end to contents of anItem
		else
			set a's originals's end to contents of anItem
		end if
	end repeat
	
	return dels
end findDuplicates

-- two todos are equal if both due dates and summaries are equal 
on elementIsInList(anElement, theList)
	using terms from application "Reminders"
		repeat with anItem in theList
			if (name of anItem is equal to name of anElement) then --and (due date of anItem is equal to due date of anElement)
				return true
			end if
		end repeat
		return false
	end using terms from
end elementIsInList




on createToDo(dueDate, theSummary, thePriority, theName)
	tell application "Reminders"
		tell list todoList of account todoAccount
			make new reminder with properties {due date:dueDate, body:theSummary, name:theName, priority:thePriority}
		end tell
	end tell
end createToDo