Understood. Maybe the ordering based on a custom field would be an option instead?
Currently, it’s single-line text. This could be changed to any other type though for the kind of information contained in these fields, in case one works better than others for scripting.
I did re-select the group, but was looking at the “added” rather than “modified” field to determine a change… Great that this already works!
we can’t use AppleScriptObjC inline in a Smart Rule script
i.e. you need to create a Script Library
we only want the Smart Rule to trigger the script one time
i.e. you need to make sure it only matches one group
Create a Script Library
In Finder:
press ⇧⌘G
paste ~/Library/Script Libraries
if no window is opening
paste ~/Library/
create folder Script Libraries
In Script Editor.app
create new document
paste this script
press ⌘S
press ⇧⌘G
paste ~/Script Libraries/
save with extension scpt
-- Script Library
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
on sortedArrayUsingDescriptors(theList, sortAscending)
try
set theArray to current application's NSMutableArray's arrayWithArray:theList
set theDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:"SortBy" ascending:sortAscending selector:"localizedStandardCompare:"
set theArray_sorted to (theArray's sortedArrayUsingDescriptors:{theDescriptor})
set theList_sorted to theArray_sorted as list
on error error_message number error_number
activate
if the error_number is not -128 then display alert "Error: Handler \"sortedArrayUsingDescriptors\"" message error_message as warning
error number -128
end try
end sortedArrayUsingDescriptors
Create a Smart Rule
Prepare group
copy the group’s reference URL (Edit > Copy Item Link)
add the reference URL to the group’s aliases (see Inspector)
Prepare script
replace property theGroup_ReferenceURL with [the group’s reference URL]
replace Custom Meta Data key with md + identifier (see Preferences > Data)
-- Smart Rule script - Create or update transclusion record
property theGroup_ReferenceURL : "x-devonthink-item://F8BC32DD-AED1-46A3-ADB3-051F5BD0DEF3" -- replace with your group's reference URL
on performSmartRule()
tell application id "DNtp"
try
set theGroup to (get record with uuid theGroup_ReferenceURL)
set theChildren to children of theGroup whose type = markdown and comment is not "Transcluded group contents"
if theChildren = {} then return
set theList to {}
repeat with thisChild in theChildren
try
set thisChild_CustomMetaData to custom meta data of thisChild
set thisChild_CustomMetaData_Value to mddescription of thisChild_CustomMetaData -- replace mddescription with "md" + your identifier (see Preferences > Data)
on error
set thisChild_CustomMetaData_Value to ""
end try
set end of theList to {|SortBy|:thisChild_CustomMetaData_Value, |RefURL|:(reference URL of thisChild), |NameWithoutExtension|:(name without extension of thisChild)}
end repeat
set theList_sorted to script "TEST"'s sortedArrayUsingDescriptors(theList, true) -- replace TEST with your Script Library's name
set theResults to search "kind:markdown comment:\"Transcluded group contents\"" in theGroup
if theResults ≠ {} then
set theTransclusionRecord to item 1 of theResults
else
set theTransclusionRecord to create record with {name:"_Transclusion - " & (name of theGroup), type:markdown, source:"", comment:"Transcluded group contents"} in theGroup
end if
set theTransclusionRecord_Source_list to {}
repeat with i from 1 to (count of theList_sorted)
set end of theTransclusionRecord_Source_list to "# " & (|NameWithoutExtension| of item i of theList_sorted) & linefeed & linefeed & "{{" & (|RefURL| of item i of theList_sorted) & "}}"
end repeat
set theTransclusionRecord_Source to my tid(theTransclusionRecord_Source_list, linefeed & linefeed & linefeed)
set plain text of theTransclusionRecord to theTransclusionRecord_Source
on error error_message number error_number
if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
return
end try
end tell
end performSmartRule
on tid(theInput, theDelimiter)
set d to AppleScript's text item delimiters
set AppleScript's text item delimiters to theDelimiter
if class of theInput = text then
set theOutput to text items of theInput
else if class of theInput = list then
set theOutput to theInput as text
end if
set AppleScript's text item delimiters to d
return theOutput
end tid
Tested and working! If anyone could actually make this viable via a smart rule, I knew it had to be you, Pete
A couple questions to better understand the inner workings:
In the instructions you wrote that the scope for the smart rule should be the parent group of the group that contains items to be transcluded. Could the scope not be the entire database as well? In this case, I think the correct group should still be identified via the criteria that the alias should contain its item link.
Are the following assumptions correct?
One separate smart rule is needed for each group, for which a transclusion should be created
The setup for each additional automated transclusion needs to be done individually in a new smart rule
More than happy to put in that prep effort upfront in exchange for full automation, just want to verify that this is indeed necessary.
Allow me to share one more idea for potential further optimization:
If the script could pull the property theGroup_ReferenceURL from the records returned by the smart rule (based on each record’s alias), then wouldn’t it potentially be possible to set up one smart rule to process multiple groups? The script would need to process each record one after the other with the steps it currently applies to the single group that is currently in the smart rule’s scope.
However, I realize this may be highly theoretical and again, the smart rule as-is will 100% address my use case. Thanks a million!!!
Yes! That way it’s possibly to do everything in one Smart Rule. I wrote the script before thinking about how the Smart Rule could match a group (i.e. did step 2 before step 1) and then obviously didn’t think about the possiblity to do everything in one Smart Rule. That’s of course far better.
copy the group’s reference URL (Edit > Copy Item Link)
add the reference URL to the group’s aliases (see Inspector)
paste the reference URL somewhere (it’s later needed in the Smart Rule)
Prepare script
replace Custom Meta Data key with md + identifier (see Preferences > Data)
replace Script Library name
Setup Smart Group
Location: the database’s root (or all databases)
Matching:
any
aliases contains [reference URL of group 1]
aliases contains [reference URL of group 2]
etc.
Action: AppleScript
-- Smart Rule script - Create or update transclusion record
on performSmartRule(theGroups)
tell application id "DNtp"
try
repeat with thisGroup in theGroups
set theChildren to (children of thisGroup whose type = markdown and comment is not "Transcluded group contents")
if theChildren ≠ {} then
set theList to {}
repeat with thisChild in theChildren
try
set thisChild_CustomMetaData to custom meta data of thisChild
set thisChild_CustomMetaData_Value to mddescription of thisChild_CustomMetaData -- replace mddescription with "md" + your identifier (see Preferences > Data)
on error
set thisChild_CustomMetaData_Value to ""
end try
set end of theList to {|SortBy|:thisChild_CustomMetaData_Value, |RefURL|:(reference URL of thisChild), |NameWithoutExtension|:(name without extension of thisChild)}
end repeat
set theList_sorted to script "TEST"'s sortedArrayUsingDescriptors(theList, true) -- replace TEST with your Script Library's name
set theResults to search "kind:markdown comment:\"Transcluded group contents\"" in thisGroup
if theResults ≠ {} then
set theTransclusionRecord to item 1 of theResults
else
set theTransclusionRecord to create record with {name:"_Transclusion - " & (name of thisGroup), type:markdown, source:"", comment:"Transcluded group contents"} in thisGroup
end if
set theTransclusionRecord_Source_list to {}
repeat with i from 1 to (count of theList_sorted)
set end of theTransclusionRecord_Source_list to "# " & (|NameWithoutExtension| of item i of theList_sorted) & linefeed & linefeed & "{{" & (|RefURL| of item i of theList_sorted) & "}}"
end repeat
set theTransclusionRecord_Source to my tid(theTransclusionRecord_Source_list, linefeed & linefeed & linefeed)
set plain text of theTransclusionRecord to theTransclusionRecord_Source
end if
end repeat
on error error_message number error_number
if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
return
end try
end tell
end performSmartRule
on tid(theInput, theDelimiter)
set d to AppleScript's text item delimiters
set AppleScript's text item delimiters to theDelimiter
if class of theInput = text then
set theOutput to text items of theInput
else if class of theInput = list then
set theOutput to theInput as text
end if
set AppleScript's text item delimiters to d
return theOutput
end tid
I have simply set the scope to “is group” and “contains x-devonthink-item in alias” to simplify even more. Of course, this only makes sense because I don’t add item links to groups’ aliases for any other reason than these transclusions.
One thing I did notice: Ordering by custom meta data field doesn’t seem to work on my end. I entered md + my field identifier, so in the script the line reads like this:
set thisChild_CustomMetaData_Value to mdinsight_relations of thisChild_CustomMetaData
I’ve also tried leaving out the md at the front, in both cases though the order of transcluded items is the same as when I enter something random as the identifier.
Any advice what I could try to troubleshoot? This would be one last component to make everything perfect.
@pete31 Quick update: I found what was causing this and have successfully worked around it now.
I was using A1, A2, A3,… up to double-digits in my custom metadata field to sort items, so at some point there is A11, A25 etc. DevonThink displays items with the above values in this order:
*A1
*A2
*A3
*A11
*A25
The order in the transclusion follows a slightly different logic. For the values above, they would be displayed like this:
A1
A11
A2
A25
A3
With this knowledge, I simply switched to using 1A, 1B, 1C, 1D, 2A, 2C etc. to order and group items. As said, it’s easy to address this, and maybe pointing it out helps someone.
Pete, I now officially declare your script as the unexpectedly perfect solution to what I was trying to accomplish. Again, truly appreciate it.
This script creates/updates a Transclusion record for the group’s supported records and selects or opens it in a new window.
Supported file types
Markdown
Text
HTML
RTF
RTFD
Formatted Note
Webarchive
Sheet
From help:
Attaching a triggered script
Select the item you want to attach a script to.
Select Tools > Get Info or Tools > Inspectors > Generic.
Click the down arrow next to Script and choose Select. Note the script can be located anywhere, but it must remain in that location for the script to trigger. For convenience, you can create a folder for them in ~/Library/Application Scripts/com.devon-technologies.think3/Menus.
-- Triggered script - Create transclusion for current group's supported records
-- Setup: Click "script" in a group's info inspecton . Select path
property selectTransclusionRecord : true -- if false Transclusion record opens in a new window
on triggered(theGroup)
tell application id "DNtp"
try
set theResults to search "kind:markdown comment:\"Transcluded group contents\"" in theGroup
if theResults ≠ {} then
set theTransclusionRecord to item 1 of theResults
else
set theTransclusionRecord to create record with {name:"_Transclusion - " & (name of theGroup), type:markdown, source:"", comment:"Transcluded group contents"} in theGroup
end if
set theChildren_ReferenceURLs to reference URL of children of theGroup whose type = markdown or type = txt or type = html or type = rtf or type = rtfd or type = formatted note or type = webarchive or type = sheet and comment is not "Transcluded group contents"
if theChildren_ReferenceURLs = {} then error "This group doesn't contain Markdown records"
set theTransclusionRecord_Source to "{{" & my tid(theChildren_ReferenceURLs, "}}" & linefeed & "{{") & "}}"
set plain text of theTransclusionRecord to theTransclusionRecord_Source
if selectTransclusionRecord then
set selection of think window 1 to {theTransclusionRecord}
else
open window for record theTransclusionRecord
activate
end if
on error error_message number error_number
if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
return
end try
end tell
end triggered
on tid(theInput, theDelimiter)
set d to AppleScript's text item delimiters
set AppleScript's text item delimiters to theDelimiter
if class of theInput = text then
set theOutput to text items of theInput
else if class of theInput = list then
set theOutput to theInput as text
end if
set AppleScript's text item delimiters to d
return theOutput
end tid
Hey @pete31, quick question:
I was wondering if it’s theoretically possible to modify the smart rule script such that it also transcludes items contained in subgroups of the group, on which the script is run?
Any hints in this direction would be greatly appreciated.
I’m using the smart rule script from this post to automatically transclude Markdown items contained in a set of groups. For each group, the script creates a transclusion document.
While the script does this perfectly for those items contained directly in the group being processed, it doesn’t transclude items contained in subgroups of the group.
If it did, this would further increase the script’s value for my use case. This is because the transclusions serve as a review document and logically, items in subgroups also need to be reviewed.
My workflow aims to extract insights from various sources, cross-links them and then classifies them under relevant tag groups in a nested hierarchy.
To ensure retention, I review the most important topics regularly. This includes having them read via text-to-speech while on the go. The contents of these reviews are topic-specific and wide-ranging.
You can add a smart rule, which will be global, to set a tag on search results. For instance, search for all documents of kind Markdown in one or more group trees. Apply the action to add a tag, and run the rule on demand.
Apply the rule from the gear menu at the bottom of the sidebar.
Set the transclusion script on the tag your smart rule adds.
When you double click the tag, you’ll see the transclusion file appear. It will physically get stored in the inbox.
Thanks for your input. This might be an approach, although it would require setting up a smart rule individually for every group to be transcluded. This means a lot of setup considering the number of groups in my database, but more importantly also regular manual maintenance.
The beauty of the smart rule script is that it’s a single smart rule that processes an unlimited number of groups and ensures there is always a current summary available. Literally no maintenance at all required.
Would you be able to share the general syntax for getting the subgroups’ children? I spent a couple hours last night trying different approaches to make it work but didn’t succeed. This is stretching my AppleScript knowledge way beyond comfort zone