Format of CSV or Delimited Export

Is there a way to export an outline in CSV or other delimited format where each top level is treated as the first field in a record and each sublevel as another field (or one field) in the same record?

For example, if the outline is:

Title 1
–Summary 1
Title 2
–Summary 2

Then the CSV would be:

Title 1, Summary 1
Title 2, Summary 2

The purpose of this would be to import the data into a spreadsheet or a database like Filemaker.

Thanks for any help!

From what I just tried, as long as Summary 1 is in a different column in OO, yes. If it is just indented or a child, it will stay in the first column of the CSV (This is not formatting well)
Title 1 (Col 1)
Summary 1 (Col 1) Text 1 (Col 2)
Summary 2 (Col 2) Text 2 (Col 3)

The second would probably give you what you want but the first will probably not.

You can do this using applescript.

Assuming no oddities in your document (ie inconsistent structure) then this should handle a basic document. I only tested it the data below so YMMV. Actually, if you are missing a L2 row in the set, it will generate an error. This can be remedied.

The script checks for the number of summary rows of the front document and then adds that many columns. If empty columns already exist then it would only add as needed. I use ‘addedCol’ for the new columns but that can be changed. FYI, the ‘topic’ column is actually column 2 as there is a special column to its left.

It then cycles through the level 1 rows (ie children of document 1) and then the children of those rows (ie level 2 rows for each level 1 row) and sets the appropriate level 1 cell to the value of the corresponding level 2 cell. It then deletes the level 2 rows and exports to a csv in the same folder.

tell application "OmniOutliner"
	set d1 to document 1
	set n1 to name of d1
	set f1 to file of d1 as text -- path including basename and extension
	tell document 1
		
		set docChildren to children -- of document 1, ie all L1
		set dclen to length of docChildren -- count of L1		
		set subCount to {} -- count of level 2 for each L1
		
		repeat with cc from 1 to dclen
			set end of subCount to count of children of item cc of docChildren
		end repeat
		set hi to my flot(subCount) -- highest number of L2 under any L1
		
		-- add columns as required		
		set colAll to columns
		set dif to (hi + 2) - (length of colAll) -- how many columns to add
		repeat with cols from 1 to dif
			make new column at end with properties {name:"addedCol"}
		end repeat
		
		-- cycle through title rows, 	add topic to newly added cells, delete summary rows
		repeat with ct in docChildren -- cycle through each title row
			set cc to 2 -- don't move data to 'topic' column (or column 1)
			
			-- cycle through each set of summary rows, set value in new cells
			repeat with cs from 1 to hi -- cycle through each summary row
				set l2topic to item cs of (get name of children of ct)
				set cc to cc + 1
				set value of (cell cc of ct) to l2topic
				
			end repeat
			delete children of ct -- delete summary rows
		end repeat
		
	end tell
end tell

-- construct export
tell application "System Events"
	-- file f1
	set csv to ".csv" -- "com.omnigroup.OmniOutliner.CSVExport.CSV"
	-- set ne to name extension of file f1
	-- set p1 to path of file f1 -- full path of file
	set cont to (path of container of file f1)
	
	set csvPath to cont & n1 & csv -- assemble full path of csv export
end tell

tell application "OmniOutliner"
	export d1 to file csvPath as "com.omnigroup.OmniOutliner.CSVExport.CSV"
end tell


-- determine most L2 rows for any L1
on flot(ldo)
	set hw to item 1 of ldo
	repeat with xx in rest of ldo
		if xx is greater than hw then
			set hw to xx
		end if
	end repeat
	return hw as integer
end flot

outline layout

Title 1
— Summary 1A
— Summary 1B
Title 2
— Summary 2A
— Summary 2B
Title 3
— Summary 3A
— Summary 3B

csv output

Topic,addedCol,addedCol
“Title 1”,“Summary 1A”,“Summary 1B”
“Title 2”,“Summary 2A”,“Summary 2B”
“Title 3”,“Summary 3A”,“Summary 3B”

2 Likes

Mockman,

I just saw your post and haven’t had a chance to try this yet, but thanks so much for the script. I’ll try to report back after running it.

Hi Mockman,

With help from ChatGPT, it appears I was able to fix the error you mention. I also removed the export part of the script, because it was giving an error and I can export manually.

However, If I may ask one more question, is it possible to have the child levels merged into one single column instead of separate columns, separated by a paragraph break? So the end result would have just two columns?

Here’s the version I have now:

tell application "OmniOutliner"
	
	set d1 to document 1 -- variable d1 now refers to the open, front OO document
	set n1 to name of d1
	set f1 to file of d1 as text -- variable f1 set to path and filename of document 1
	
	tell document 1
		
		set docChildren to children -- var docChildren is a list of all outline L1 of document 1
		set dclen to length of docChildren -- count of L1		
		set subCount to {} -- variable subCount set to an empty list count of level 2 for each L1
		
		repeat with cc from 1 to dclen
			set end of subCount to count of children of item cc of docChildren
			--Add a value to the end of the subCount list
			--The value is the total number (count) of L2 (children) of the current L1   
		end repeat
		
		set hi to my flot(subCount) -- highest number of L2 under any L1
		
		-- add columns as required		
		set colAll to columns
		set dif to (hi + 2) - (length of colAll) -- how many columns to add
		repeat with cols from 1 to dif
			make new column at end with properties {name:"addedCol"}
		end repeat
		
		-- cycle through title rows, add topic to newly added cells, delete summary rows
		repeat with ct in docChildren -- cycle through each title row
			set cc to 2 -- don't move data to 'topic' column (or column 1)
			
			-- cycle through each set of summary rows, set value in new cells
			set l2 to children of ct
			repeat with cs from 1 to hi -- cycle through each summary row
				if cs ≤ (count of l2) then
					set l2topic to item cs of (get name of children of ct)
					set cc to cc + 1
					set value of (cell cc of ct) to l2topic
				end if
			end repeat
			delete children of ct -- delete summary rows
		end repeat
		
	end tell
end tell

--Determine most L2 rows for any L1
on flot(ldo)
	set hw to item 1 of ldo
	repeat with xx in rest of ldo
		if xx is greater than hw then
			set hw to xx
		end if
	end repeat
	return hw as integer
end flot

This is actually easier to solve. This script should work regardless of the number of summary rows under each child. It uses a linefeed character but you could also use a carriage return. Basically, what it does is make a list of the summaries under each title and then join them on the delimiter, which is set to linefeed.

Title 1
— Summary 1A
Title 2
— Summary 2A
— Summary 2B
Title 3
— Summary 3A
— Summary 3B
— Summary 3C
— Summary 3D

tell application "OmniOutliner"
	set d1 to document 1 -- variable d1 now refers to the open, front OO document
	set n1 to name of d1
	set f1 to file of d1 as text -- variable f1 set to path and filename of document 1
	tell document 1
		-- add new column
		make new column at end with properties {name:"addedCol"}
		-- get title rows (all L1)
		set docChildren to children -- var docChildren is a list of all outline L1 of document 1
		
		-- cycle through title rows; add summaries to new cell; delete summary children
		repeat with ti in docChildren
			set sumo to "" -- reset summary block
			set summs to {} -- collect the name/topic of each summary row under this title
			set end of summs to name of children of ti -- add summary to list
			set AppleScript's text item delimiters to linefeed -- use linefeed as delimiter
			set sumo to summs as text -- join items of summary list into block of text
			set value of last cell of ti to sumo -- set last cell of title to summary block
			delete children of ti -- delete summary children
		end repeat
		
	end tell
end tell
set AppleScript's text item delimiters to "" -- clear delimiters

You can add the export segment of the original script at the bottom once you figure out how to get it to work.

And what error on the export?

Output would look like this:

Topic,addedCol
“Title 1”,“Summary 1A”
“Title 2”,“Summary 2A
Summary 2B”
“Title 3”,“Summary 3A
Summary 3B
Summary 3C
Summary 3D”

1 Like

Thanks! I’ll try this and write back later today.

The export error was related to being unable to write the file, something about it not being a writable file type. But it’s possible that ChatGPT changed something I didn’t notice. I’ll try again and post the error if it recurrs.

Alright, the OO transformation part is working. The only change I’ve made is to change the delimiter from “linefeed” to “linefeed & linefeed”, so that there’s a blank line between each paragraph of the second column.

However, the export still produces an error. It appears to be in the penultimate line. The error is: “OmniOutliner got an error: “com.omnigroup.OmniOutliner.CSVExport.CSV” is not a writable file type.” number 6.

Here’s the complete script:

type or paste code here
```tell application "OmniOutliner"
	
	-- Variable definitions
	
	set d1 to document 1 -- variable d1 now refers to the open, front OO document
	set n1 to name of d1
	set f1 to file of d1 as text -- variable f1 set to path and filename of document 1
	
	-- Manipulate OO document
	
	tell document 1
		
		-- add new column
		make new column at end with properties {name:"addedCol"}
		
		-- get title rows (all L1)
		set docChildren to children -- var docChildren is a list of all outline L1 of doc 1
		
		-- cycle through title rows; add summaries to new cell; delete summary children
		repeat with ti in docChildren
			set sumo to "" -- reset summary block
			set summs to {} -- collect the name/topic of each summary row under this title
			set end of summs to name of children of ti -- add summary to list
			set AppleScript's text item delimiters to linefeed & linefeed -- use linefeed as delimiter
			set sumo to summs as text -- join items of summary list into block of text
			set value of last cell of ti to sumo -- set last cell of title to summary block
			delete children of ti -- delete summary children
		end repeat
		
	end tell
end tell

set AppleScript's text item delimiters to "" -- clear delimiters

-- construct export

tell application "System Events"
	
	set csv to ".csv" -- "com.omnigroup.OmniOutliner.CSVExport.CSV"
	
	set cont to (path of container of file f1)
	
	set csvPath to cont & n1 & csv -- assemble full path of csv export
	
end tell

tell application "OmniOutliner"
	
	export d1 to file csvPath as "com.omnigroup.OmniOutliner.CSVExport.CSV"
end tell

Create a new script, put just this line in it and run. Look for ‘csv’ within the results. What do you find?

tell application "OmniOutliner" to get writable document types

Your script version works for me, as is.

1 Like

This is the result:

{document type “com.omnigroup.omnioutliner.xmlooutline” of application “OmniOutliner”, document type “public.plain-text” of application “OmniOutliner”, document type “public.plain-text” of application “OmniOutliner”, document type “com.omnigroup.omnioutliner.xmlooutline-package” of application “OmniOutliner”, document type “com.omnigroup.omnioutliner.otemplate” of application “OmniOutliner”, document type “com.omnigroup.omnioutliner.otemplate-package” of application “OmniOutliner”, document type “com.omnigroup.omnioutliner.oo3” of application “OmniOutliner”, document type “com.omnigroup.omnioutliner.oo3-package” of application “OmniOutliner”, document type “com.omnigroup.omnioutliner.oo3template” of application “OmniOutliner”, document type “com.omnigroup.omnioutliner.oo3template-package” of application “OmniOutliner”, document type “org.opml.opml” of application “OmniOutliner”, document type “org.opml.opmltemplate” of application “OmniOutliner”, document type “public.rtf” of application “OmniOutliner”, document type “com.symantec.more.text” of application “OmniOutliner”, document type “public.plain-text” of application “OmniOutliner”, document type “com.apple.rtfd” of application “OmniOutliner”, document type “com.omnigroup.word.openxml.indented” of application “OmniOutliner”, document type “com.omnigroup.word.openxml.outline” of application “OmniOutliner”, document type “com.omnigroup.omnioutliner.simplehtmlexport.html” of application “OmniOutliner”, document type “com.microsoft.powerpoint.openxml.presentation” of application “OmniOutliner”, document type “public.plain-text” of application “OmniOutliner”, document type “com.omnigroup.omnioutliner.htmlexport.htmldynamic” of application “OmniOutliner”, document type “com.microsoft.excel.openxml.document” of application “OmniOutliner”, document type “public.comma-separated-values-text” of application “OmniOutliner”}

The last item is the only one I see that refers to CSV.

Try that one.

I have an old version of OO (5.3.4) and apparently, they have modified the string although perhaps it all derives from apple making changes in the OS.

1 Like

It appears to be working now. I added a few lines to have an Excel export as well. I will paste the complete script below in case anyone else is interested.

Thank you so much for all your help. This is very, very useful.

(*
Turns an OmniOutliner document with one column of Level 1 and child levels into a document with one column for L1 and a second column combining all child levels.

It also exports the result to a CSV file and an Excel file.

Should be run after placing a duplicate of the OO document in a folder (so that the original is not affected), and only once the document is open in OO and is the front document.
*)

tell application "OmniOutliner"
	
	-- Variable definitions
	
	set d1 to document 1 -- variable d1 now refers to the open, front OO document
	set n1 to name of d1
	set f1 to file of d1 as text -- variable f1 set to path and filename of document 1
	
	-- Manipulate OO document
	
	tell document 1
		
		-- add new column
		make new column at end with properties {name:"addedCol"}
		
		-- get title rows (all L1)
		set docChildren to children -- var docChildren is a list of all outline L1 of doc 1
		
		-- cycle through title rows; add summaries to new cell; delete summary children
		repeat with ti in docChildren
			set sumo to "" -- reset summary block
			set summs to {} -- collect the name/topic of each summary row under this title
			set end of summs to name of children of ti -- add summary to list
			set AppleScript's text item delimiters to linefeed & linefeed -- use 2 linefeeds as delimiter
			set sumo to summs as text -- join items of summary list into block of text
			set value of last cell of ti to sumo -- set last cell of title to summary block
			delete children of ti -- delete summary children
		end repeat
		
	end tell
end tell

set AppleScript's text item delimiters to "" -- clear delimiters

-- construct export

tell application "System Events"
	
	set csv to ".csv" -- "com.omnigroup.OmniOutliner.CSVExport.CSV"
	
	set exl to ".xlsx"
	
	set cont to (path of container of file f1)
	
	set csvPath to cont & n1 & csv -- assemble full path of csv export
	
	set exlPath to cont & n1 & exl -- assemble full path of Excel export
	
end tell

tell application "OmniOutliner"
	
	export d1 to file csvPath as "public.comma-separated-values-text"
	export d1 to file exlPath as "com.microsoft.excel.openxml.document"
	
end tell

Glad it worked out.

Per your comments at the top of the final script… you could get the script to do those things (duplicate, open, work on a script by name, bring doc to front).