Script "Tag from this list" finished version

I want to write a script to limit my tagging to certain sub-group of tags within the current database. combining a tread in this forum A “tag selector”, akin to “group selector” and search for a sorting handler from internet, I am almost there (this script allows multiple tags selection at once, which is convenient), BUT are getting stuck in two lines of code. I hope someone will help me out there:
Thank you very much in advance

The original code is to extract all tags of current database:

set theTags to (name of every parent of current database whose (tag type is ordinary tag))

But I want to call up a specific parent group of tags, get the uuid of this group, and get the name of all Childs under all levels (that are ordinary tags) under the UUID:

        repeat
		   set parentTag to display name editor info "Name of Parent tag:"
		   if parentTag is not "" then exit repeat
		end repeat
		set parentTagLocation to "/Tags/" & parentTag
		set recUUID to uuid of record parentTagLocation of current database -- problem (1)
        
        set parentTag to (get record with uuid recUUID) 
		set theTags to (name of every child of parentTag whose (tag type is ordinary tag)) -- problem (2)

Problem (1) The recUUID always return with the UUID of trash?
Problem (2) EDITED: The modified line of code now extract all tags that are the immediate child of parentTag, but not “all” ordinary tags under the parentTag (some of them are more than one level down)

The full script as follow:

tell application id "DNtp"
	
	activate
	try
		set this_selection to the selection
		if this_selection is {} then error "Please select some contents."
		
		-- original code
		-- set theTags to (name of every parent of current database whose (tag type is ordinary tag))
		
		-- my code: ask for the parent tag group
		repeat
			set parentTag to display name editor info "Name of Parent tag:"
			if parentTag is not "" then exit repeat
		end repeat
		set parentTagLocation to "/Tags/" & parentTag
		set recUUid to uuid of record parentTagLocation of current database
		set parentTag to (get record with uuid recUUID) 
		set theTags to (name of every child of parentTag whose (tag type is ordinary tag)) -- problem (2)
		
		set theSortTags to my sortlist(theTags)
		
		set myTags to (choose from list theSortTags with prompt {"Choose tags to apply:"} default items "" with multiple selections allowed)
		
		repeat with this_item in this_selection
			if exists tags of this_item then
				set current_tags to the tags of this_item
				set new_tags to current_tags & myTags
			else
				set new_tags to myTags
			end if
			set the tags of this_item to new_tags
		end repeat
		
	on error error_message number error_number
		if the error_number is not -128 then
			try
				display alert "DEVONthink Pro" message error_message as warning
			on error number error_number
				if error_number is -1708 then display dialog error_message buttons {"OK"} default button 1
			end try
		end if
	end try
end tell

on sortlist(theList)
	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
	return theSortedList
end sortlist

I rest my case. (1) found out how to do it in a clumsy way (works regardless) and (2) unrealistic usage scenario, it’s clumsy to ask for the name of parent tag for every item I want to tag.
So (1) it makes more sense to hardcode the UUID of the parent tag I want to choose, or (2) perhaps I will write another small script to select and save the name of a parent tag to a text file, then this script will look for the name/uuid in that file. Method (2) is perhaps more dynamic that (1).
Thanks anyway

Modified with hardcoding the parent tag’s UUID:

        Set SubjectTag to "353B55FB-D23F-4F2C-A076-78EDDF1F9XY2"
		set ParentTagUUID to SubjectTag
		set parentTag to (get record with uuid ParentTagUUID)
		set theTags to (name of every child of parentTag) whose (tag type is ordinary tag)

The line…

set recUUID to uuid of record parentTagLocation of current database -- problem (1)

…isn’t valid but this should work:

set theRecord to get record at parentTagLocation
set recUUID to uuid of theRecord

Thank you very much!
Re problem (2), is there a way to get all tags under the parentTag? My current code can only get the child that is immediately under parentTag. Thank again.

I think I find a smarter way than hardcoding the uuid in this script. I have written another setup script to popup a list and choose which parent tag I would like to construct the list from in this script, and its uuid is saved to the finder comment of the top level tag (right under the database and it won’t change). So, this script only need to read the comment of the top level tag to know which set of tags I would like to activate for tagging. If I need to switch to another list for another round of review/dimension of tagging I just need to run the setup once more. IMHO, multiple tagging at once from a list seems faster and easier AS LONG AS no new tag is needed (in my case and when I know which list of tags is the focus).

It’s possible but you have to check the complete hierarchy on your own like…

on getTags(theParent, theTags)
	tell application id "DNtp"
		repeat with theChild in children of theParent
			if tag type of theChild is ordinary tag then
				set theTags to theTags & theChild
				my getTags(theChild,theTags)
			end
		end repeat
	end tell
end getTags
1 Like

Got it. It’s all good. I have all the material to work on a better version of the script.

Thank you very much!

What this script does: I want to limit my tagging to a constrained list of tags in certain situations and for various reasons. This script will call up an existing list of tags that are under a parent tag of user’s choice, and allow multiple tags assignment all at once - as long as those tags are under the chosen parent tag.

A complete version of the script. But it’ll require some setup and customisation to each user’s preference. There are two scripts:
Script 1 is to setup the default parent tag to list the sub-tags. The script 1 is executed on need basis and only if user want to change the default list for tagging. Therefore, it’s best to put it under script menu.
Script 2 is to popup a list of tags for user to make multiple tagging all at once from a constrained list. A property is available in script 2 to set whether the list is to include tags at all levels or just one level below the parent tag. It’s better to assign a short-cut or as a button on toolbar.
PS: if there is any error, you may need to test it, and search and seek answer from this forum. I probably are not capable to give you a good answer.

Script 1

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

-- Original idea from https://discourse.devontechnologies.com/t/a-tag-selector-akin-to-group-selector/9755/8
-- Modified by Ngan 2019.07.01

-- TopTag is the highest level of tag for all possible subgroups of tags you may want to use this function. This tag will normally be "Excluded from tagging". 
-- Only for tags that is immediately under topTag can be a parentTag (chosen in this script). Change the code as anyone sees fit.
-- You will need to copy and paste the uuid of your topTag
property TopTagUUID : "982054C4-1C90-47DC-BAB8-DA095C37E077"

tell application id "DNtp"
	--	activate
	try		
		set TopTag to (get record with uuid TopTagUUID)
		set TopTagPath to (get location of TopTag) & (name of TopTag)
        -- the subgroups as available choices of parent group. 
		set theTags to (name of every child of TopTag)
        -- sort theTags list by alphabetical order
		set theSortTags to my sortlist(theTags)
        --  since this setup is to choose the parent tag, only one choice is allowed
		repeat
			set myTags to (choose from list theSortTags with prompt {"Choose tags to apply:"} default items "")
			if myTags is not "" then exit repeat
		end repeat
		
		set thisTagPath to TopTagPath & "/" & myTags
		set thisTagUUID to get uuid of (get record at thisTagPath)
        -- save the uuid of the chosen parent tag to the finder comment of the TopTag so user won't have to choose the parent group for the tagging of every item!
		set comment of TopTag to ""
		set comment of TopTag to thisTagUUID
	end try
end tell

on sortlist(theList)
	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
	return theSortedList
end sortlist

Script 2

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

-- Original from https://discourse.devontechnologies.com/t/a-tag-selector-akin-to-group-selector/9755/8
-- Modified by Ngan 2019.07.01

-- TopTag is the highest level of tag for all possible subgroups of tags you may want to use this function. This tag will normally be "Excluded from tagging". 
-- All tags right under topTag can be a parentTag (chosen in the script 1)

-- This uuid of your topTag should be the same as the one in script 1
property topTagUUID : "982054C4-1C90-47DC-BAB8-DA095C37E077" 
property allLevel : true -- true if user wants to choose from child tags of all levels from the parent tag, false if only immediate child tags are wanted

tell application id "DNtp"
	
	activate
	try
		set this_selection to the selection
		if this_selection is {} then error "Please select some contents."
		
		-- The uuid of parentTag is stored in the comment field of the topTag so there is no need to specify/find it. Use script 1 to change it if needed.
		set theTopTag to (get record with uuid topTagUUID)
		set ParentTagUUID to (comment of theTopTag)
		set parentTag to (get record with uuid ParentTagUUID)
		
		
		-- Original code to retrieve ALL tags in tags of the current database - which is too many!
		-- set theTags to (name of every parent of current database whose (tag type is ordinary tag))
		
		if allLevel is true then
			-- Get all child tags at all levels below the parent tag
			set theTags to my gettags(parentTag)
		else
			-- Get only the tags that are the immediate children of the parent tag
			set theTags to (name of every child of parentTag) whose (tag type is ordinary tag)
		end if
		
		-- sort the list
		set theSortTags to my sortlist(theTags)

		-- this list allows multiple tagging all at once
		set myTags to (choose from list theSortTags with prompt {"Choose tags to apply:"} default items "" with multiple selections allowed)
		
		
		repeat with this_item in this_selection
			if exists tags of this_item then
				set current_tags to the tags of this_item
				set new_tags to current_tags & myTags
			else
				set new_tags to myTags
			end if
			set the tags of this_item to new_tags
		end repeat
		
	on error error_message number error_number
		if the error_number is not -128 then
			try
				display alert "DEVONthink Pro" message error_message as warning
			on error number error_number
				if error_number is -1708 then display dialog error_message buttons {"OK"} default button 1
			end try
		end if
	end try
end tell

on gettags(theParent)
	local theTagList, tagName
	set theTagList to {}
	tell application id "DNtp"
		repeat with theChild in children of theParent
			if tag type of theChild is ordinary tag then
				set tagName to the name of theChild
				set theTagList to theTagList & tagName
				set theTagList to theTagList & my gettags(theChild)
			end if
		end repeat
	end tell
	return theTagList
end gettags

on sortlist(theList)
	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
	return theSortedList
end sortlist

Hi!

I’m trying to modify this function (using a list instead of a string for the theTags variable) to get the children of a parent tag. I do so, by selecting a tag in the tag viewer:
image

With the selected tag, i run the following code:

	
	if selection is {} then
		-- if no selection is made, process all records of the database
		set recordList to every record of current database
		-- If a selection is made, process selected records
	else if selection is not {} then
		set recordList to selection as list
	end if

	set theParent to item 1 of recordList
	set theTags to {}

	repeat with theChild in children of theParent
		if tag type of theChild is ordinary tag then
			set the end of theTags to theChild
			--my getTags(theChild, theTags)
		end if
	end repeat
	get name of item 1 of theTags

I then get the result “kvitto”. Further, getting all of the tag names seems to work as expected. (N.B. that I needed to comment the part “my getTags(theChild, theTags)” since I’m not calling this from within a function)

If I instead call the following function:

on getTags(theParent, theTags)
	tell application id "DNtp"
		repeat with theChild in children of theParent
			if tag type of theChild is ordinary tag then
				set the end of theTags to theChild
				my getTags(theChild, theTags)
			end if
		end repeat
	end tell
end getTags

I get the error:
image
Translating roughly to:
"Script error
“DEVONthink 3 experienced an error: Cannot continue getTags.”

I also get the following text in the result panel:
Resultat:

error “DEVONthink 3 drabbades av ett fel: Kan inte fortsätta getTags.” number -1708

I have tried to understand what the error code could mean, or if there’s something wrong elsewhere, but I cannot understand what the problem is. I’ve also tried adding this statement in the top of the script

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

Without luck.

Any help would be appreciated.

Regards,
Björn

Perhaps it will be easier for others to offer help if you post the entire script and explain what you want to achieve. Sometimes, the error is coming from somewhere unexpected in a script. You can also try to break your script flow into smaller steps, and try to debug step by step (assuming that you have a debugger that can run the script step-by-step or you know how to use the std script editor to debug in which I don’t).

For example, it might be easier to debug the getTags handler by breaking the repeat statement into two steps, and on a stand alone basis:
(1) first check whether u are really getting the record of “theParent”
(2) Check whether “l” is getting the first level children of the parent.
If both (1) and (2) and the handler is working normally, then the error is coming from other place.
(3) Check whether allTags contains all tags in ur database

If everything works after (1) to (3), then it is the other coding lines…

tell application id "DNtp"
	set theParent to get record at "/Tags"
	set allTags to my getAllChildTags(theParent, "")
	
	
end tell


on getAllChildTags(theParent, theTabs)
	local l, theTagList, tagName
	set theTagList to {}
	tell application id "DNtp"
		set l to children of theParent whose tag type is ordinary tag
		repeat with theChild in l
			set theTagList to theTagList & ((theTabs & name of theChild) as string)
			set theTagList to theTagList & my getAllChildTags(theChild, theTabs & "	")
		end repeat
	end tell
	return theTagList
end getAllChildTags

Thank you for the offered help, ngan, I really appreciate it and the posts you’ve done here - they’re really inspiring!

Sorry if it’s hard to follow. I’ve spent quite a few hours trying to find the error, and I also try to break down the problem to get the reason for the handler not working - or rather, not being able to call the handler. I now realised the reason for the error - i needed to append the “of me” to the call, like this:

getTags(theParent, theTags) of me

Now I can start playing with the handler again.

Ah… yes

When u are calling handlers within any “tell” block that are not part of the functions of the “tell” application, u need to use “add() of me”, or “my abc()”. I couldn’t figure it out when I first started writing handlers until I start reading reference book.

If you are using script debugger (3 weeks trial, I think), the debugger would have stopped right at the handler’s code line and show u a message of “Can’t continue getAllChildTags.” so that you know the script can’t even recognise the handler.

Could now verify the function, works like a charm! How did you learn AppleScript? Could you recommend a book?

Thanks again!

Other experienced members will give you different and better options coz learning is a matter of personal style.

For me (bear in mind that I am just a beginner), I have read three excellent books: AppleScript 123 (Soghoian), Learn AppleScript (Sanderson) and my favourite for a later stage reading: AppleScript (Neuberg). I think AppleScript 123 is a very good starting point. However, an informative and functional debugger will also help to speed up the learning process a lot coz it helps to understand the DT’s elements throughout the debugging process.

Cheers

Thanks a lot!