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