I have been using Nirvana for my task list. I’d like to try OmniFocus. To get there, I had to convert my data.

First step is to export Nirvana data to CSV from their web site. The export isn’t perfect. They don’t identify which reference-items belong to which reference lists, for example.

Then import, using the following AppleScript. IT isn’t perfect, but it is a good start.

set theFile to (choose file with prompt "Select input file:" of type {"csv"})

global myOnHold, myActive, myDone, singletonProject, whenCowsComeHome
set taskLimit to 99000
set whenCowsComeHome to date "Thursday, December 31, 2099 at 1:00:00 PM"

set RefListName to "Reference Lists"
set RefListManualName to "Reference Item to Manually Move to Reference List"
tell application "OmniFocus"
	set myOnHold to on hold
	set myActive to active
	set myDone to done
end tell

set progress total steps to 1
set progress additional description to "Starting up"
-- set progress completed steps to 1

-- First item is just column names, and it may contain a UTF BOM
set firstTime to true
set debugging to false
set myCounter to 1

set refList to mkFolder(RefListName)
set refListBad to mkProject(RefListManualName, myOnHold, "")
set singletonProject to mkProject("Miscellaneous", myActive, "")
tell application "OmniFocus" to tell default document to set singleton action holder of singletonProject to true
tell application "OmniFocus" to tell default document to set default singleton action holder of singletonProject to true

-- I use att and kpk as "Areas of Responsibility"
set kpkList to mkFolder("kpk")
set attList to mkFolder("att")

set csvText to csvToList(read theFile, {})
set progress completed steps to 1

set progress total steps to count of csvText
set progress additional description to "Projects"

with timeout of 300 seconds
	
	---------------------------------------------------------------------
	-- first time through, create projects
	---------------------------------------------------------------------
	repeat with anItem in csvText
		if not firstTime then
			set {aType, aParent, aState, aCompleted, aFocus, aName, aTags, aTime, aEnergy, aWaitingFor, aStartDate, aDueDate, aNotes} to text items of anItem
			if aType is "Project" and aState is not "Trash" then
				if aState = "" then
					-- these are reference lists (Standalone), or items within reference lists (Standalone*).
					if aParent = "Standalone" then
						set newProj to mkProject(aName, myOnHold, aNotes)
						-- handleFocus(aFocus, newProj)  -- I do not focus my reference lists
						-- handleCompleted(aCompleted, newProj)  -- reference lists never get marked complete!
						moveItemToFolderName(newProj, RefListName)
					end if
				else
					set newProj to mkProject(aName, nirvanaStatusToOmniFocus(aState), aNotes)
					handleFocus(aFocus, newProj)
					handleCompleted(aCompleted, newProj)
					
					-- I use att and kpk as "Areas of Responsibility"
					set splitTags to split(aTags, ",")
					if splitTags contains "att" then
						moveItemToFolderName(newProj, "att")
					else if splitTags contains "kpk" then
						moveItemToFolderName(newProj, "kpk")
					end if
				end if
			end if
		end if
		set firstTime to false
		set progress completed steps to myCounter
		set progress additional description to "Projects, record " & myCounter as text
		set myCounter to myCounter + 1
	end repeat
	
	-- display dialog "Completed projects.  Continue to Tasks?" giving up after 30000
	
	set firstTime to true
	set myCounter to 1
	set progress total steps to count of csvText
	set progress additional description to "Tasks"
	
	---------------------------------------------------------------------
	-- next time through, create tasks
	---------------------------------------------------------------------
	repeat with anItem in csvText
		if not firstTime then
			set {aType, aParent, aState, aCompleted, aFocus, aName, aTags, aTime, aEnergy, aWaitingFor, aStartDate, aDueDate, aNotes} to text items of anItem
			if aState is not "Trash" then
				if aType is "Project" and aState is "" and aParent is "Standalone*" then
					-- these are reference lists (Standalone), or items within reference lists (Standalone*).
					--				display dialog "adding reference item:" & aName giving up after 30000
					tell application "OmniFocus" to tell refListBad to set newTask to make new task with properties {name:aName, defer date:whenCowsComeHome}
					-- handleCompleted(aCompleted, newTask) -- do not "complete" reference list items.
					-- handleFocus(aFocus, newTask)  -- I do not focus my reference lists.
					moveTaskToProjectBasedOnTag(newTask, aTags)
					
				else if aType is equal to "Task" then
					set newTask to mkTask(aName, aParent, aState, aTags)
					handleFocus(aFocus, newTask)
					handleStartDate(aStartDate, newTask)
					handleDueDate(aDueDate, newTask)
					handleTaskNote(aNotes, newTask)
					if aState is "Someday" and aStartDate is "" then
						tell application "OmniFocus" to tell default document to set defer date of newTask to (current date) + weeks * 56 + (days * (random number from 1 to 30))
					end if
					if aState is "Inactive/Later" and aStartDate is "" then
						tell application "OmniFocus" to tell default document to set defer date of newTask to (current date) + (days * 14) + (days * (random number from 1 to 7))
					end if
					handleCompleted(aCompleted, newTask)
				end if
			end if
		end if
		set firstTime to false
		set progress completed steps to myCounter
		set progress additional description to "Tasks, record " & myCounter as text
		set myCounter to myCounter + 1
		if myCounter > taskLimit then
			display dialog "Quit early at task limit" giving up after 30000
			error number -128
		end if
	end repeat
	display dialog "Caution.  \nThe repeat pattern on repeating tasks is not exported by Nirvana, so I can't import that.  These items have had ' #REPEATING' appended  to the subject.\r\n\n* Nirvana does not export sequetian/parallel.  All projects have been marked sequential (because I'm feeling overwhelmed).\n* Tags have been dropped, because OmniFocus does not do tags.\n* Someday tasks are brought in with a start date of now + 13 months plus a random number of days (so you don't have to deal with them all at once)\n* 'Later' tasks come in as startDate of now + 2 weeks plus a small random number of days." giving up after 30000
end timeout

(* 
  From Nigel Garvey at http://macscripter.net/viewtopic.php?id=32322 ...
  Assumes that the CSV text adheres to the convention:
   Records are delimited by LFs or CRLFs (but CRs are also allowed here).
   The last record in the text may or may not be followed by an LF or CRLF (or CR).
   Fields in the same record are separated by commas (unless specified differently by parameter).
   The last field in a record must not be followed by a comma.
   Trailing or leading spaces in unquoted fields are not ignored (unless so specified by parameter).
   Fields containing quoted text are quoted in their entirety, any space outside them being ignored.
   Fields enclosed in double-quotes are to be taken verbatim, except for any included double-quote pairs, which are to be translated as double-quote characters.
       
   No other variations are currently supported. *)

on csvToList(csvText, implementation)
	-- The 'implementation' parameter must be a record. Leave it empty ({}) for the default assumptions: ie. comma separator, leading and trailing spaces in unquoted fields not to be trimmed. Otherwise it can have a 'separator' property with a text value (eg. {separator:tab}) and/or a 'trimming' property with a boolean value ({trimming:true}).
	set {separator:separator, trimming:trimming} to (implementation & {separator:",", trimming:false})
	
	script o -- Lists for fast access.
		property qdti : getTextItems(csvText, "\"")
		property currentRecord : {}
		property possibleFields : missing value
		property recordList : {}
	end script
	
	-- o's qdti is a list of the CSV's text items, as delimited by double-quotes.
	-- Assuming the convention mentioned above, the number of items is always odd.
	-- Even-numbered items (if any) are quoted field values and don't need parsing.
	-- Odd-numbered items are everything else. Empty strings in odd-numbered slots
	-- (except at the beginning and end) indicate escaped quotes in quoted fields.
	
	set astid to AppleScript's text item delimiters
	set qdtiCount to (count o's qdti)
	set quoteInProgress to false
	considering case
		repeat with i from 1 to qdtiCount by 2 -- Parse odd-numbered items only.
			set thisBit to item i of o's qdti
			if ((count thisBit) > 0) or (i is qdtiCount) then
				-- This is either a non-empty string or the last item in the list, so it doesn't
				-- represent a quoted quote. Check if we've just been dealing with any.
				if (quoteInProgress) then
					-- All the parts of a quoted field containing quoted quotes have now been
					-- passed over. Coerce them together using a quote delimiter.
					set AppleScript's text item delimiters to "\""
					set thisField to (items a thru (i - 1) of o's qdti) as string
					-- Replace the reconstituted quoted quotes with literal quotes.
					set AppleScript's text item delimiters to "\"\""
					set thisField to thisField's text items
					set AppleScript's text item delimiters to "\""
					-- Store the field in the "current record" list and cancel the "quote in progress" flag.
					set end of o's currentRecord to thisField as string
					set quoteInProgress to false
				else if (i > 1) then
					-- The preceding, even-numbered item is a complete quoted field. Store it.
					set end of o's currentRecord to item (i - 1) of o's qdti
				end if
				
				-- Now parse this item's field-separator-delimited text items, which are either non-quoted fields or stumps from the removal of quoted fields. Any that contain line breaks must be further split to end one record and start another. These could include multiple single-field records without field separators.
				set o's possibleFields to getTextItems(thisBit, separator)
				set possibleFieldCount to (count o's possibleFields)
				repeat with j from 1 to possibleFieldCount
					set thisField to item j of o's possibleFields
					if ((count thisField each paragraph) > 1) then
						-- This "field" contains one or more line endings. Split it at those points.
						set theseFields to thisField's paragraphs
						-- With each of these end-of-record fields except the last, complete the field list for the current record and initialise another. Omit the first "field" if it's just the stub from a preceding quoted field.
						repeat with k from 1 to (count theseFields) - 1
							set thisField to item k of theseFields
							if ((k > 1) or (j > 1) or (i is 1) or ((count trim(thisField, true)) > 0)) then set end of o's currentRecord to trim(thisField, trimming)
							set end of o's recordList to o's currentRecord
							set o's currentRecord to {}
						end repeat
						-- With the last end-of-record "field", just complete the current field list if the field's not the stub from a following quoted field.
						set thisField to end of theseFields
						if ((j < possibleFieldCount) or ((count thisField) > 0)) then set end of o's currentRecord to trim(thisField, trimming)
					else
						-- This is a "field" not containing a line break. Insert it into the current field list if it's not just a stub from a preceding or following quoted field.
						if (((j > 1) and ((j < possibleFieldCount) or (i is qdtiCount))) or ((j is 1) and (i is 1)) or ((count trim(thisField, true)) > 0)) then set end of o's currentRecord to trim(thisField, trimming)
					end if
				end repeat
				
				-- Otherwise, this item IS an empty text representing a quoted quote.
			else if (quoteInProgress) then
				-- It's another quote in a field already identified as having one. Do nothing for now.
			else if (i > 1) then
				-- It's the first quoted quote in a quoted field. Note the index of the
				-- preceding even-numbered item (the first part of the field) and flag "quote in
				-- progress" so that the repeat idles past the remaining part(s) of the field.
				set a to i - 1
				set quoteInProgress to true
			end if
		end repeat
	end considering
	
	-- At the end of the repeat, store any remaining "current record".
	if (o's currentRecord is not {}) then set end of o's recordList to o's currentRecord
	set AppleScript's text item delimiters to astid
	
	return o's recordList
end csvToList

-- Get the possibly more than 4000 text items from a text.
on getTextItems(txt, delim)
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to delim
	set tiCount to (count txt's text items)
	set textItems to {}
	repeat with i from 1 to tiCount by 4000
		set j to i + 3999
		if (j > tiCount) then set j to tiCount
		set textItems to textItems & text items i thru j of txt
	end repeat
	set AppleScript's text item delimiters to astid
	
	return textItems
end getTextItems

-- From Nigel Garvey at http://macscripter.net/viewtopic.php?id=32322 ...
-- Trim any leading or trailing spaces from a string.
on trim(txt, trimming)
	if (trimming) then
		repeat with i from 1 to (count txt) - 1
			if (txt begins with space) then
				set txt to text 2 thru -1 of txt
			else
				exit repeat
			end if
		end repeat
		repeat with i from 1 to (count txt) - 1
			if (txt ends with space) then
				set txt to text 1 thru -2 of txt
			else
				exit repeat
			end if
		end repeat
		if (txt is space) then set txt to ""
	end if
	
	return txt
end trim

on mkFolder(fname)
	with timeout of 300 seconds
		tell application "OmniFocus"
			tell front document
				if not (folder fname exists) then
					make new folder with properties {name:fname}
				end if
				return first folder whose name is fname
			end tell
		end tell
	end timeout
end mkFolder

on mkProject(pname, pstatus, pnotes)
	with timeout of 300 seconds
		
		tell application "OmniFocus"
			tell front document
				if (flattened project pname exists) then
					return first flattened project whose name is pname
				end if
				set tmpProject to make new project with properties {name:pname, status:pstatus, note:pnotes}
				--				if pnotes is not "" then
				--					set note of tmpProject to pnotes
				--				end if
				
				set sequential of tmpProject to true
				return tmpProject
			end tell
		end tell
	end timeout
end mkProject

on nirvanaStatusToOmniFocus(aState)
	if aState is "Active" then
		return myActive
	else if aState is "Logbook" then
		return myDone
	else if aState is "Someday" then
		return myOnHold
	else
		display dialog "Unable to convert state:" & aState giving up after 30000
		error number -128
	end if
end nirvanaStatusToOmniFocus

on moveItemToFolderName(aProj, aFolder)
	with timeout of 300 seconds
		
		tell application "OmniFocus"
			tell front document
				move aProj to (end of sections of (first folder whose name is aFolder))
			end tell
		end tell
	end timeout
end moveItemToFolderName

on split(theString, theDelimiter)
	-- save delimiters to restore old settings
	set oldDelimiters to AppleScript's text item delimiters
	-- set delimiters to delimiter to be used
	set AppleScript's text item delimiters to theDelimiter
	-- create the array
	set theArray to every text item of theString
	-- restore the old setting
	set AppleScript's text item delimiters to oldDelimiters
	-- return the result
	return theArray
end split


on moveTaskToProject(aTask, aProjectName)
	with timeout of 300 seconds
		-- set progress additional description to aProjectName
		if isProjectName(aProjectName) then
			tell application "OmniFocus"
				tell default document
					set theProject to (first flattened project whose name is aProjectName)
					move aTask to end of tasks of theProject
				end tell
			end tell
		end if
	end timeout
end moveTaskToProject

on moveTaskToProjectBasedOnTag(aTask, aTags)
	with timeout of 300 seconds
		set myList to my split(aTags, ",")
		repeat with theItem in myList
			if isProjectName(theItem) then
				tell application "OmniFocus"
					tell default document
						set theProject to (first flattened project whose name is theItem)
						move aTask to end of tasks of theProject
					end tell
				end tell
				return
			end if
		end repeat
	end timeout
	display dialog "Nirvana does not identify the parent of a reference item in the export.  If you will tag the ITEMS (not the containing list) with the name of the reference list (exactly), that will also tag the items and  I can use that to put it in the right list." giving up after 30000
	error number -128
end moveTaskToProjectBasedOnTag


on isProjectName(aName)
	with timeout of 300 seconds
		-- handle the special case, testing for a literal, because it is faster than talking to OmniFocus
		if aName is "Standalone" then
			return false
		end if
		
		tell application "OmniFocus"
			tell default document
				if flattened project aName exists then
					return true
				else
					return false
				end if
			end tell
		end tell
	end timeout
end isProjectName

-- It is an active task if Parent is Standalone or project; state is Next or Inbox
on mkTask(aName, inParent, aState, aTags)
	with timeout of 300 seconds
		set tmpName to aName
		if aState is "Scheduled/Repeating" then
			set tmpName to aName & " #REPEATING"
		end if
		
		set aParent to inParent
		if aParent is "Standalone" or aParent is "Standalone*" then
			set aParent to "Miscellaneous"
		end if
		
		tell application "OmniFocus"
			tell default document
				if flattened project aParent exists then
					set interestingProject to first flattened project whose name is aParent
					if task tmpName of interestingProject exists then
						set interestingTask to first task of interestingProject whose name is tmpName
						delete interestingTask
					end if
				end if
				set newTask to make new inbox task with properties {name:tmpName}
			end tell
		end tell
		
		if aState is "Inbox" then
			-- nothing.  Leave it in Inbox, where it was created
		else if aState is "Next" or aState is "Scheduled" or aState is "Scheduled/Repeating" or aState is "Someday" or aState is "Inactive/Later" or aState is "Logbook" then
			moveTaskToProject(newTask, aParent)
		else
			display dialog "Unexpected state:" & aState & " for task:" & aName giving up after 30000
		end if
		
		if aState is "Logbook" then
			tell application "OmniFocus"
				tell default document
					set completed of newTask to true
				end tell
			end tell
		end if
		
	end timeout
	return newTask
end mkTask

--  convert yyyy-mm-dd to mm/dd/yyyy STRING
on parseNirvanaDate(nvDate)
	set yyyy to text 1 thru 4 of nvDate
	set mm to text 6 thru 7 of nvDate
	set dd to text 9 thru 10 of nvDate
	return date (mm & "/" & dd & "/" & yyyy & " 1:00:00 PM")
end parseNirvanaDate


on handleFocus(aFocus, aItem)
	with timeout of 300 seconds
		if aFocus is "Yes" then
			tell application "OmniFocus" to tell default document to set flagged of aItem to true
		end if
	end timeout
end handleFocus

on handleCompleted(aCompleted, aItem)
	-- if class of aItem as string is "project" then
	--	display dialog ("aCompleted:" & aCompleted & ", class:" & class of aItem as string) & ", " & name of aItem
	-- end if
	with timeout of 300 seconds
		if aCompleted is not "" then
			tell application "OmniFocus"
				tell default document
					if (class of aItem as string) is "project" then
						set status of aItem to done
					else
						set completed of aItem to true
					end if
					set completion date of aItem to my parseNirvanaDate(aCompleted)
				end tell
			end tell
			
		end if
	end timeout
end handleCompleted

on handleStartDate(aStartDate, aItem)
	with timeout of 300 seconds
		if aStartDate is not "" then
			tell application "OmniFocus" to tell default document to set defer date of aItem to my parseNirvanaDate(aStartDate)
		end if
	end timeout
end handleStartDate

on handleDueDate(aDueDate, aItem)
	with timeout of 300 seconds
		if aDueDate is not "" then
			set tmpDate to parseNirvanaDate(aDueDate)
			tell application "OmniFocus"
				tell default document
					set due date of aItem to tmpDate
				end tell
			end tell
		end if
	end timeout
end handleDueDate


on handleTaskNote(aNotes, aTask)
	with timeout of 300 seconds
		tell application "OmniFocus" to tell default document to set note of aTask to aNotes
	end timeout
end handleTaskNote

</code>