Script: Create or change smart group(s)

Script: Create or change smart group(s)

This script creates smart groups in selected group(s) or if nothing is selected a database-global smart group. It creates a meta group to hold database-global smart groups and a meta smart group which collects all group-specific smart groups.

2

If a smart group is selected it can be used to change the query and/or name. That’s especially useful to change existing smart group names to their query. To do so add a space to the query in the query dialog, this opens the name dialog. Also handy to update the name after changing a query via smart group editor. I find using the query as name (or part of the name) makes learning the syntax much easier.

As syntax mistakes happen quite easy and often over here there’s a help button which opens DEVONthink’s “search prefixes” page in the default browser. Property theHelpFileOrRefURL accepts a reference URL or URL too so a custom help resource could be used.

1

If text is selected it is used as default answer.

There are different modes to reveal/open created smart groups, however it’s not easy (for me) to choose one as each has advantages and disadvantages depending on the situation.

To make the meta group stick out a thumbnail can be set in a template record, “Smart” in the first capture is actually a group with a smart group thumbnail.

By the way @ngan made a script for normal searches.

-- Create or change smart group(s)

property theMetaGroupName : "Smart"
property theSmartGroupName : "_Subsets"
property revealInCurrentWindow : true
property openInCurrentWindow : false
property openInNewWindow : false
property openInNewWindows : false
property theHelpFileOrRefURL : ((POSIX path of (path to application id "DNtp") as text) & "/Contents/Resources/DEVONthink.help/Contents/Resources/pgs/appendix-searchprefixes.html") as string
property theIconPath : (((path to application id "DNtp") & "Contents:Resources:DEVONthink 3.icns:") as string) as alias
property theTemplateThumbnailUUID : ""

tell application id "DNtp"
	try
		set theSearchGroups to selection of viewer window 1
		
		if (count theSearchGroups) = 1 then
			set theType to type of item 1 of theSearchGroups as string
			if (theType = "smart group" or theType = "«constant ****DTsg»") then
				my updateSmartGroup(item 1 of theSearchGroups)
				return
			end if
		end if
		
		set scopeDatabase to false
		set theDatabase to current database
		if theSearchGroups = {} then
			set theSearchGroups to root of window 1
			try
				set theSearchGroups to theSearchGroups as list
			on error
				display alert "Hoppla!" buttons {"OK"} default button 1 message "Mehrfachauswahl in Seitenleiste nicht möglich." as informational
				return
			end try
			set scopeDatabase to true
			set theType to type of item 1 of theSearchGroups as string
			if theType = "group" or theType = "«constant ****DTgr»" then set scopeDatabase to false
			if uuid of item 1 of theSearchGroups = (uuid of theDatabase) then set scopeDatabase to true
		else
			repeat with thisSearchGroup in theSearchGroups
				set theType to type of thisSearchGroup as string
				if theType = "group" or theType = "«constant ****DTgr»" then set scopeDatabase to false
			end repeat
		end if
		
		try
			set selectedText to selected text of window 1 & "" as string
			set defaultAnswer to selectedText
			set scopeDatabase to true
			set theSearchGroups to {theDatabase}
		on error
			set defaultAnswer to ""
		end try
		set dialogButtons to {"Hilfe", "Abbrechen", "OK"}
		set theDialog to display dialog "Suche:" default answer defaultAnswer buttons dialogButtons default button 3 with title "" with icon theIconPath
		set {theButton, theQuery} to {button returned, text returned} of theDialog
		if theButton = item 1 of dialogButtons then
			do shell script "open " & quoted form of theHelpFileOrRefURL
			return
		else if theButton = item 2 of dialogButtons then
			return
		else
			if theQuery = "" then return
		end if
		
		set theMetaGroup to create location ("/" & theMetaGroupName) in theDatabase
		if theTemplateThumbnailUUID ≠ "" then set thumbnail of theMetaGroup to thumbnail of (get record with uuid theTemplateThumbnailUUID)
		
		if scopeDatabase = true then
			set theSubsetsSmartGroup to {}
			set theSmartGroups to (smart groups of theDatabase whose location is ("/" & theMetaGroupName & "/") and search predicates = theQuery)
			if theSmartGroups = {} then
				set theSmartGroup to create record with {type:smart group, search predicates:theQuery, search group:item 1 of theSearchGroups, name:theQuery, exclude from search:true} in theMetaGroup
			else
				set theSmartGroup to item 1 of theSmartGroups
			end if
		else
			if not (exists record at ("/" & theMetaGroupName & "/" & theSmartGroupName) in theDatabase) then
				set theSubsetsSmartGroup to create record with {type:smart group, search predicates:"kind:smart group", search group:theDatabase, name:theSmartGroupName, exclude from search:true} in theMetaGroup
			else
				set theSubsetsSmartGroup to get record at ("/" & theMetaGroupName & "/" & theSmartGroupName) in theDatabase
			end if
			set newSmartGroups to {}
			repeat with thisSearchGroup in theSearchGroups
				set theSmartGroups to (smart groups of theDatabase whose location is (location of thisSearchGroup & name of thisSearchGroup & "/") and search predicates = theQuery)
				if theSmartGroups = {} then
					set theSmartGroup to create record with {type:smart group, search predicates:theQuery, search group:thisSearchGroup, name:theQuery & " in: " & name of thisSearchGroup} in thisSearchGroup
				else
					set theSmartGroup to item 1 of theSmartGroups
				end if
				set end of newSmartGroups to theSmartGroup
			end repeat
		end if
		
		if revealInCurrentWindow = true then
			if (count theSearchGroups) = 1 then
				set selection of window 1 to {theSmartGroup}
			else
				set root of window 1 to theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInCurrentWindow = true then
			if (count theSearchGroups) = 1 then
				set root of window 1 to theSmartGroup
			else
				set root of window 1 to theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInNewWindow = true then
			if (count theSearchGroups) = 1 then
				open window for record theSmartGroup
			else
				open window for record theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInNewWindows = true then
			if (count theSearchGroups) = 1 then
				open window for record theSmartGroup
			else
				repeat with thisSmartGroup in newSmartGroups
					open window for record thisSmartGroup
				end repeat
			end if
		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

on updateSmartGroup(theRecord)
	tell application id "DNtp"
		try
			set {theName, theQuery} to {name, search predicates} of theRecord
			set dialogButtons to {"Hilfe", "Abbrechen", "OK"}
			set theDialog to display dialog theQuery default answer theQuery buttons dialogButtons default button 3 with title theName with icon theIconPath
			set {theButton, newQuery} to {button returned, text returned} of theDialog
			if theButton = item 1 of dialogButtons then
				do shell script "open " & quoted form of theHelpFileOrRefURL
				return
			else if theButton = item 2 of dialogButtons then
				return
			else
				considering case
					if newQuery = theQuery or newQuery = "" then return
				end considering
				set search predicates of theRecord to newQuery
				set dialogButtons to {"Abbrechen", "OK"}
				set theDialog to display dialog "Name:" default answer newQuery buttons dialogButtons default button 2 with title theName with icon theIconPath
				set {theButton, newName} to {button returned, text returned} of theDialog
				if theButton = item 1 of dialogButtons then
					return
				else if theButton = item 2 of dialogButtons then
					if newName = "" then return
					if location of theRecord = ("/" & theMetaGroupName & "/") then
						set name of theRecord to newName
					else
						set name of theRecord to newName & " in: " & name of parent 1 of theRecord
					end if
				end if
			end if
			
		on error error_message number error_number
			error number -128
		end try
	end tell
end updateSmartGroup

2 Likes

This

& this

are very handy!

1 Like

Updated the script. If no group is selected it creates a database-global smart group. Especially useful to create new smart groups when a smart group is selected in the sidebar.

Updated. Now also uses raw syntax to check the record type.

In case you tried to run the script you’ve found that it doesn’t work as expected in DEVONthink 3.6.

There’s a problem with creating Smart Groups via AppleScript, see Are Smart Groups in DEVONthink 3.6 broken?.

Updated the script with a workaround.

-- Create or change smart group(s)

property theMetaGroupName : "Smart"
property theSmartGroupName : "_Subsets"
property revealInCurrentWindow : true
property openInCurrentWindow : false
property openInNewWindow : false
property openInNewWindows : false
property theHelpFileOrRefURL : ((POSIX path of (path to application id "DNtp") as text) & "/Contents/Resources/DEVONthink.help/Contents/Resources/pgs/appendix-searchprefixes.html") as string
property theIconPath : (((path to application id "DNtp") & "Contents:Resources:DEVONthink 3.icns:") as string) as alias
property theTemplateThumbnailUUID : ""

tell application id "DNtp"
	try
		set theSearchGroups to selection of viewer window 1
		
		if (count theSearchGroups) = 1 then
			set theType to type of item 1 of theSearchGroups as string
			if (theType = "smart group" or theType = "«constant ****DTsg»") then
				my updateSmartGroup(item 1 of theSearchGroups)
				return
			end if
		end if
		
		set scopeDatabase to false
		set theDatabase to current database
		if theSearchGroups = {} then
			set theSearchGroups to root of window 1
			try
				set theSearchGroups to theSearchGroups as list
			on error
				display alert "Hoppla!" buttons {"OK"} default button 1 message "Mehrfachauswahl in Seitenleiste nicht möglich." as informational
				return
			end try
			set scopeDatabase to true
			set theType to type of item 1 of theSearchGroups as string
			if theType = "group" or theType = "«constant ****DTgr»" then set scopeDatabase to false
			if uuid of item 1 of theSearchGroups = (uuid of theDatabase) then set scopeDatabase to true
		else
			repeat with thisSearchGroup in theSearchGroups
				set theType to type of thisSearchGroup as string
				if theType = "group" or theType = "«constant ****DTgr»" then set scopeDatabase to false
			end repeat
		end if
		
		try
			set selectedText to selected text of window 1 & "" as string
			set defaultAnswer to selectedText
			set scopeDatabase to true
			set theSearchGroups to {theDatabase}
		on error
			set defaultAnswer to ""
		end try
		set dialogButtons to {"Hilfe", "Abbrechen", "OK"}
		set theDialog to display dialog "Suche:" default answer defaultAnswer buttons dialogButtons default button 3 with title "" with icon theIconPath
		set {theButton, theQuery} to {button returned, text returned} of theDialog
		if theButton = item 1 of dialogButtons then
			do shell script "open " & quoted form of theHelpFileOrRefURL
			return
		else if theButton = item 2 of dialogButtons then
			return
		else
			if theQuery = "" then return
		end if
		
		set theMetaGroup to create location ("/" & theMetaGroupName) in theDatabase
		if theTemplateThumbnailUUID ≠ "" then set thumbnail of theMetaGroup to thumbnail of (get record with uuid theTemplateThumbnailUUID)
		
		if scopeDatabase = true then
			set theSubsetsSmartGroup to {}
			set theSmartGroups to (smart groups of theDatabase whose location is ("/" & theMetaGroupName & "/") and search predicates = theQuery)
			if theSmartGroups = {} then
				set theSmartGroup to create record with {type:smart group, search predicates:theQuery, name:theQuery, exclude from search:true} in theMetaGroup
				set search group of theSmartGroup to (item 1 of theSearchGroups)
			else
				set theSmartGroup to item 1 of theSmartGroups
			end if
		else
			if not (exists record at ("/" & theMetaGroupName & "/" & theSmartGroupName) in theDatabase) then
				set theSubsetsSmartGroup to create record with {type:smart group, search predicates:"kind:smart group", name:theSmartGroupName, exclude from search:true} in theMetaGroup
				set search group of theSubsetsSmartGroup to theDatabase
			else
				set theSubsetsSmartGroup to get record at ("/" & theMetaGroupName & "/" & theSmartGroupName) in theDatabase
			end if
			set newSmartGroups to {}
			repeat with thisSearchGroup in theSearchGroups
				set theSmartGroups to (smart groups of theDatabase whose location is (location of thisSearchGroup & name of thisSearchGroup & "/") and search predicates = theQuery)
				if theSmartGroups = {} then
					set theSmartGroup to create record with {type:smart group, search predicates:theQuery, name:theQuery & " in: " & name of thisSearchGroup} in thisSearchGroup
					set search group of theSmartGroup to thisSearchGroup
				else
					set theSmartGroup to item 1 of theSmartGroups
				end if
				set end of newSmartGroups to theSmartGroup
			end repeat
		end if
		
		if revealInCurrentWindow = true then
			if (count theSearchGroups) = 1 then
				set selection of window 1 to {theSmartGroup}
			else
				set root of window 1 to theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInCurrentWindow = true then
			if (count theSearchGroups) = 1 then
				set root of window 1 to theSmartGroup
			else
				set root of window 1 to theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInNewWindow = true then
			if (count theSearchGroups) = 1 then
				open window for record theSmartGroup
			else
				open window for record theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInNewWindows = true then
			if (count theSearchGroups) = 1 then
				open window for record theSmartGroup
			else
				repeat with thisSmartGroup in newSmartGroups
					open window for record thisSmartGroup
				end repeat
			end if
		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

on updateSmartGroup(theRecord)
	tell application id "DNtp"
		try
			set {theName, theQuery} to {name, search predicates} of theRecord
			set dialogButtons to {"Hilfe", "Abbrechen", "OK"}
			set theDialog to display dialog theQuery default answer theQuery buttons dialogButtons default button 3 with title theName with icon theIconPath
			set {theButton, newQuery} to {button returned, text returned} of theDialog
			if theButton = item 1 of dialogButtons then
				do shell script "open " & quoted form of theHelpFileOrRefURL
				return
			else if theButton = item 2 of dialogButtons then
				return
			else
				considering case
					if newQuery = theQuery or newQuery = "" then return
				end considering
				set search predicates of theRecord to newQuery
				set dialogButtons to {"Abbrechen", "OK"}
				set theDialog to display dialog "Name:" default answer newQuery buttons dialogButtons default button 2 with title theName with icon theIconPath
				set {theButton, newName} to {button returned, text returned} of theDialog
				if theButton = item 1 of dialogButtons then
					return
				else if theButton = item 2 of dialogButtons then
					if newName = "" then return
					if location of theRecord = ("/" & theMetaGroupName & "/") then
						set name of theRecord to newName
					else
						set name of theRecord to newName & " in: " & name of parent 1 of theRecord
					end if
				end if
			end if
			
		on error error_message number error_number
			error number -128
		end try
	end tell
end updateSmartGroup
1 Like

Thanks for this!
I’m trying to change the script to make a smart group with other criterias, like “Tag is WORD” (WORD is the input I gave to the script when prompted) instead of “All matches WORD”, see the change I want to do to the resulting smart group in the prints. I have no experience with Applescript so I cannot really figure out how. Would it be possible and how?

Best would be if I could give multiple inputs, like “Tag is X or Y”, “Name is Z” etc.

From this:

To this:

No need to change it, the script can be used with any query.

It’s easiest if you

  • test your query in a toolbar search to see whether it matches what you want

  • use the query in the script’s dialog.

Use tag:WORD

Use {any: tags:X;Y} name:Z

1 Like

Thanks! In my case the most convenient would be if I didn’t had to do the “smart querying” but only answer the question “what tag?” but your solution will work if that’s not possible.
/Lasse

This should do it. Just enter the tags, separated by semicolon.

Setup

Set property useAnyTag to add the any: search prefix:

  • if true then {any: tags:X;Y}
  • if false then tags:X;Y
Click to see the script
-- Create or change Tags smart group(s)

property theMetaGroupName : "Smart"
property theSmartGroupName : "_Subsets"
property revealInCurrentWindow : true
property openInCurrentWindow : false
property openInNewWindow : false
property openInNewWindows : false
property theHelpFileOrRefURL : ((POSIX path of (path to application id "DNtp") as text) & "/Contents/Resources/DEVONthink.help/Contents/Resources/pgs/appendix-searchprefixes.html") as string
property theIconPath : (((path to application id "DNtp") & "Contents:Resources:DEVONthink 3.icns:") as string) as alias
property theTemplateThumbnailUUID : ""

property useAnyTag : false -- use {any: tags:" instead of "tags:"

tell application id "DNtp"
	try
		set theSearchGroups to selection of viewer window 1
		
		if (count theSearchGroups) = 1 then
			set theType to type of item 1 of theSearchGroups as string
			if (theType = "smart group" or theType = "«constant ****DTsg»") then
				my updateSmartGroup(item 1 of theSearchGroups)
				return
			end if
		end if
		
		set scopeDatabase to false
		set theDatabase to current database
		if theSearchGroups = {} then
			set theSearchGroups to root of window 1
			try
				set theSearchGroups to theSearchGroups as list
			on error
				display alert "Hoppla!" buttons {"OK"} default button 1 message "Mehrfachauswahl in Seitenleiste nicht möglich." as informational
				return
			end try
			set scopeDatabase to true
			set theType to type of item 1 of theSearchGroups as string
			if theType = "group" or theType = "«constant ****DTgr»" then set scopeDatabase to false
			if uuid of item 1 of theSearchGroups = (uuid of theDatabase) then set scopeDatabase to true
		else
			repeat with thisSearchGroup in theSearchGroups
				set theType to type of thisSearchGroup as string
				if theType = "group" or theType = "«constant ****DTgr»" then set scopeDatabase to false
			end repeat
		end if
		
		try
			set selectedText to selected text of window 1 & "" as string
			set defaultAnswer to selectedText
			set scopeDatabase to true
			set theSearchGroups to {theDatabase}
		on error
			set defaultAnswer to ""
		end try
		set dialogButtons to {"Hilfe", "Abbrechen", "OK"}
		set theDialog to display dialog "Suche:" default answer defaultAnswer buttons dialogButtons default button 3 with title "" with icon theIconPath
		set {theButton, theQuery_Tags} to {button returned, text returned} of theDialog
		if theButton = item 1 of dialogButtons then
			do shell script "open " & quoted form of theHelpFileOrRefURL
			return
		else if theButton = item 2 of dialogButtons then
			return
		else
			if theQuery_Tags = "" then return
		end if
		
		if useAnyTag then
			set theQuery to "{any: tags:" & theQuery_Tags & "}"
		else
			set theQuery to "tags:" & theQuery_Tags
		end if
		
		set theMetaGroup to create location ("/" & theMetaGroupName) in theDatabase
		if theTemplateThumbnailUUID ≠ "" then set thumbnail of theMetaGroup to thumbnail of (get record with uuid theTemplateThumbnailUUID)
		
		if scopeDatabase = true then
			set theSubsetsSmartGroup to {}
			set theSmartGroups to (smart groups of theDatabase whose location is ("/" & theMetaGroupName & "/") and search predicates = theQuery)
			if theSmartGroups = {} then
				set theSmartGroup to create record with {type:smart group, search predicates:theQuery, name:theQuery, exclude from search:true} in theMetaGroup
				set search group of theSmartGroup to (item 1 of theSearchGroups)
			else
				set theSmartGroup to item 1 of theSmartGroups
			end if
		else
			if not (exists record at ("/" & theMetaGroupName & "/" & theSmartGroupName) in theDatabase) then
				set theSubsetsSmartGroup to create record with {type:smart group, search predicates:"kind:smart group", name:theSmartGroupName, exclude from search:true} in theMetaGroup
				set search group of theSubsetsSmartGroup to theDatabase
			else
				set theSubsetsSmartGroup to get record at ("/" & theMetaGroupName & "/" & theSmartGroupName) in theDatabase
			end if
			set newSmartGroups to {}
			repeat with thisSearchGroup in theSearchGroups
				set theSmartGroups to (smart groups of theDatabase whose location is (location of thisSearchGroup & name of thisSearchGroup & "/") and search predicates = theQuery)
				if theSmartGroups = {} then
					set theSmartGroup to create record with {type:smart group, search predicates:theQuery, name:theQuery & " in: " & name of thisSearchGroup} in thisSearchGroup
					set search group of theSmartGroup to thisSearchGroup
				else
					set theSmartGroup to item 1 of theSmartGroups
				end if
				set end of newSmartGroups to theSmartGroup
			end repeat
		end if
		
		if revealInCurrentWindow = true then
			if (count theSearchGroups) = 1 then
				set selection of window 1 to {theSmartGroup}
			else
				set root of window 1 to theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInCurrentWindow = true then
			if (count theSearchGroups) = 1 then
				set root of window 1 to theSmartGroup
			else
				set root of window 1 to theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInNewWindow = true then
			if (count theSearchGroups) = 1 then
				open window for record theSmartGroup
			else
				open window for record theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInNewWindows = true then
			if (count theSearchGroups) = 1 then
				open window for record theSmartGroup
			else
				repeat with thisSmartGroup in newSmartGroups
					open window for record thisSmartGroup
				end repeat
			end if
		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

on updateSmartGroup(theRecord)
	tell application id "DNtp"
		try
			set {theName, theQuery} to {name, search predicates} of theRecord
			set dialogButtons to {"Hilfe", "Abbrechen", "OK"}
			
			set theQueryItems to my tid(theQuery, {"tags:", "}"})
			set theQuery_Tags to item 2 of theQueryItems
			
			set theDialog to display dialog theQuery default answer theQuery_Tags buttons dialogButtons default button 3 with title theName with icon theIconPath
			set {theButton, newQuery_Tags} to {button returned, text returned} of theDialog
			
			if useAnyTag then
				set newQuery to "{any: tags:" & newQuery_Tags & "}"
			else
				set newQuery to "tags:" & newQuery_Tags
			end if
			
			if theButton = item 1 of dialogButtons then
				do shell script "open " & quoted form of theHelpFileOrRefURL
				return
			else if theButton = item 2 of dialogButtons then
				return
			else
				considering case
					if newQuery = theQuery or newQuery = "" then return
				end considering
				set search predicates of theRecord to newQuery
				set dialogButtons to {"Abbrechen", "OK"}
				set theDialog to display dialog "Name:" default answer newQuery buttons dialogButtons default button 2 with title theName with icon theIconPath
				set {theButton, newName} to {button returned, text returned} of theDialog
				if theButton = item 1 of dialogButtons then
					return
				else if theButton = item 2 of dialogButtons then
					if newName = "" then return
					if location of theRecord = ("/" & theMetaGroupName & "/") then
						set name of theRecord to newName
					else
						set name of theRecord to newName & " in: " & name of parent 1 of theRecord
					end if
				end if
			end if
			
		on error error_message number error_number
			error number -128
		end try
	end tell
end updateSmartGroup

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

Here’s another version. This one lets you choose tags via choose from list.

It might be slow if you have a lot of tags.

Setup

Set property useAnyTag to add the any: search prefix:

  • if true then {any: tags:X;Y}
  • if false then tags:X;Y
Click to see the script
-- Create or change Tags smart group(s) via choose from list

property theMetaGroupName : "Smart"
property theSmartGroupName : "_Subsets"
property revealInCurrentWindow : false
property openInCurrentWindow : false
property openInNewWindow : true
property openInNewWindows : false
property theHelpFileOrRefURL : ((POSIX path of (path to application id "DNtp") as text) & "/Contents/Resources/DEVONthink.help/Contents/Resources/pgs/appendix-searchprefixes.html") as string
property theIconPath : (((path to application id "DNtp") & "Contents:Resources:DEVONthink 3.icns:") as string) as alias
property theTemplateThumbnailUUID : ""

property useAnyTag : true -- use {any: tags:" instead of "tags:"

tell application id "DNtp"
	try
		set theSearchGroups to selection of viewer window 1
		
		if (count theSearchGroups) = 1 then
			set theType to type of item 1 of theSearchGroups as string
			if (theType = "smart group" or theType = "«constant ****DTsg»") then
				my updateSmartGroup(item 1 of theSearchGroups)
				return
			end if
		end if
		
		set scopeDatabase to false
		set theDatabase to current database
		if theSearchGroups = {} then
			set theSearchGroups to root of window 1
			try
				set theSearchGroups to theSearchGroups as list
			on error
				display alert "Hoppla!" buttons {"OK"} default button 1 message "Mehrfachauswahl in Seitenleiste nicht möglich." as informational
				return
			end try
			set scopeDatabase to true
			set theType to type of item 1 of theSearchGroups as string
			if theType = "group" or theType = "«constant ****DTgr»" then set scopeDatabase to false
			if uuid of item 1 of theSearchGroups = (uuid of theDatabase) then set scopeDatabase to true
		else
			repeat with thisSearchGroup in theSearchGroups
				set theType to type of thisSearchGroup as string
				if theType = "group" or theType = "«constant ****DTgr»" then set scopeDatabase to false
			end repeat
		end if
		
		set theQuery_Tags_list to my chooseTagsFromList(theDatabase, {})
		set theQuery_Tags to my tid(theQuery_Tags_list, ";")
		
		if useAnyTag then
			set theQuery to "{any: tags:" & theQuery_Tags & "}"
		else
			set theQuery to "tags:" & theQuery_Tags
		end if
		
		set theMetaGroup to create location ("/" & theMetaGroupName) in theDatabase
		if theTemplateThumbnailUUID ≠ "" then set thumbnail of theMetaGroup to thumbnail of (get record with uuid theTemplateThumbnailUUID)
		
		if scopeDatabase = true then
			set theSubsetsSmartGroup to {}
			set theSmartGroups to (smart groups of theDatabase whose location is ("/" & theMetaGroupName & "/") and search predicates = theQuery)
			if theSmartGroups = {} then
				set theSmartGroup to create record with {type:smart group, search predicates:theQuery, name:theQuery, exclude from search:true} in theMetaGroup
				set search group of theSmartGroup to (item 1 of theSearchGroups)
			else
				set theSmartGroup to item 1 of theSmartGroups
			end if
		else
			if not (exists record at ("/" & theMetaGroupName & "/" & theSmartGroupName) in theDatabase) then
				set theSubsetsSmartGroup to create record with {type:smart group, search predicates:"kind:smart group", name:theSmartGroupName, exclude from search:true} in theMetaGroup
				set search group of theSubsetsSmartGroup to theDatabase
			else
				set theSubsetsSmartGroup to get record at ("/" & theMetaGroupName & "/" & theSmartGroupName) in theDatabase
			end if
			set newSmartGroups to {}
			repeat with thisSearchGroup in theSearchGroups
				set theSmartGroups to (smart groups of theDatabase whose location is (location of thisSearchGroup & name of thisSearchGroup & "/") and search predicates = theQuery)
				if theSmartGroups = {} then
					set theSmartGroup to create record with {type:smart group, search predicates:theQuery, name:theQuery & " in: " & name of thisSearchGroup} in thisSearchGroup
					set search group of theSmartGroup to thisSearchGroup
				else
					set theSmartGroup to item 1 of theSmartGroups
				end if
				set end of newSmartGroups to theSmartGroup
			end repeat
		end if
		
		if revealInCurrentWindow = true then
			if (count theSearchGroups) = 1 then
				set selection of window 1 to {theSmartGroup}
			else
				set root of window 1 to theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInCurrentWindow = true then
			if (count theSearchGroups) = 1 then
				set root of window 1 to theSmartGroup
			else
				set root of window 1 to theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInNewWindow = true then
			if (count theSearchGroups) = 1 then
				open window for record theSmartGroup
			else
				open window for record theSubsetsSmartGroup
				set selection of window 1 to newSmartGroups
			end if
		else if openInNewWindows = true then
			if (count theSearchGroups) = 1 then
				open window for record theSmartGroup
			else
				repeat with thisSmartGroup in newSmartGroups
					open window for record thisSmartGroup
				end repeat
			end if
		end if
		
		activate
		
	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

on updateSmartGroup(theRecord)
	tell application id "DNtp"
		try
			set {theName, theQuery} to {name, search predicates} of theRecord
			
			set theQueryItems to my tid(theQuery, {"tags:", "}"})
			set theQuery_Tags to item 2 of theQueryItems
			set theQuery_Tags_list to my tid(theQuery_Tags, ";")
			
			set newQuery_Tags_list to my chooseTagsFromList(database of theRecord, theQuery_Tags_list)
			set newQuery_Tags to my tid(newQuery_Tags_list, ";")
			
			if useAnyTag then
				set newQuery to "{any: tags:" & newQuery_Tags & "}"
			else
				set newQuery to "tags:" & newQuery_Tags
			end if
			
			set search predicates of theRecord to newQuery
			set dialogButtons to {"Abbrechen", "OK"}
			set theDialog to display dialog "Name:" default answer newQuery buttons dialogButtons default button 2 with title theName with icon theIconPath
			set {theButton, newName} to {button returned, text returned} of theDialog
			if theButton = item 1 of dialogButtons then
				return
			else if theButton = item 2 of dialogButtons then
				if newName = "" then return
				if location of theRecord = ("/" & theMetaGroupName & "/") then
					set name of theRecord to newName
				else
					set name of theRecord to newName & " in: " & name of parent 1 of theRecord
				end if
			end if
			
		on error error_message number error_number
			error number -128
		end try
	end tell
end updateSmartGroup

on chooseTagsFromList(theDatabase, theChooseFromListItems_Arguments_old)
	try
		tell application id "DNtp"
			set theTags_Locations to location of parents of theDatabase whose location starts with "/Tags/"
			set theTags_Locations_Count to count of theTags_Locations
			if theTags_Locations_Count = 0 then error "Current database: No Tags"
			set theTags_Names to name of parents of theDatabase whose location starts with "/Tags/"
			
			set theChooseFromListItems to {}
			
			set theChooseFromListDefaultItems to {}
			
			repeat with i from 1 to theTags_Locations_Count
				set thisTag_Location to item i of theTags_Locations
				if thisTag_Location = "/Tags/" then
					set thisTag_Location_DisplayedLocation to ""
				else
					set thisTag_Location_DisplayedLocation to (characters 7 thru -1 in thisTag_Location) as string
				end if
				set thisTag_Name to item i of theTags_Names
				set thisChooseFromListItem to (thisTag_Location_DisplayedLocation & thisTag_Name & linefeed & thisTag_Name) as string
				set end of theChooseFromListItems to thisChooseFromListItem
				if thisTag_Name is in theChooseFromListItems_Arguments_old then set end of theChooseFromListDefaultItems to thisChooseFromListItem
			end repeat
			
			if theTags_Locations_Count > 2 then
				set theChooseFromListItems to my sort_list(theChooseFromListItems)
			end if
			
			set theChoice to choose from list theChooseFromListItems with prompt "Choose Tags" default items theChooseFromListDefaultItems with title "Create Smart Group(s)" with multiple selections allowed
			if theChoice is false then error number -128
			
			set theQuery_Tags to {}
			
			repeat with thisChooseFromListItem in theChoice
				set thisChooseFromListItem_Argument to paragraph 2 of thisChooseFromListItem
				set end of theQuery_Tags to thisChooseFromListItem_Argument
			end repeat
			
			return theQuery_Tags
			
		end tell
	on error error_message number error_number
		activate
		if the error_number is not -128 then display alert "Error: Handler \"chooseTagsFromList\"" message error_message as warning
		error number -128
	end try
end chooseTagsFromList

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

on sort_list(theList)
	considering numeric strings
		set theIndexList to {}
		set theSortedList to {}
		repeat (length of theList) times
			set theLowItem to ""
			repeat with a from 1 to (length of theList)
				if a is not in theIndexList then
					set theCurrentItem to item a of theList as text
					if theLowItem is "" then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					else if theCurrentItem comes before theLowItem then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					end if
				end if
			end repeat
			set end of theSortedList to theLowItem
			set end of theIndexList to theLowItemIndex
		end repeat
	end considering
	return theSortedList
end sort_list

1 Like

Wow, thanks so much! Very helpful. I’ve now begun to learn a bit applescript to use with DT (I so wish one could use python), if you have any suggestion on where to head for a useful online class or alike please let me know.
Best,
Lasse

I’m feeling your pain – but you could use JavaScript. Not Python, but probably closer to Python than AppleScript.

6 posts were split to a new topic: JavaScript: Create or change smart group