System.tag.browse on an perspective object prop

I am trying to bind a perspective dropdown component value prop to the results of the sys.tag.browse function, I can get a messy batch pretty easily by using a script transformation on a tag path like this:

value = '[default]Customers/Demo/nampa_ls/Reads'
new_value = system.tag.browse(value, {}).getResults()
return new_value

Returns: 
[
  {
    "fullPath": "[default]Customers/Demo/nampa_ls/Reads/Pump 2",
    "hasChildren": true,
    "name": "Pump 2",
    "tagType": "Folder"
  },
  {
    "fullPath": "[default]Customers/Demo/nampa_ls/Reads/WW_Level",
    "hasChildren": false,
    "dataType": "Float4",
    "name": "WW_Level",
    "tagType": "AtomicTag",
    "attributes": "[]",
    "value": 27.941008
  },
  {
    "fullPath": "[default]Customers/Demo/nampa_ls/Reads/Pump 1",
    "hasChildren": true,
    "name": "Pump 1",
    "tagType": "Folder"
  }
]

This will return the one item I have in that folder and the names of the two folders inside of it.
However, what I need it to do is to do a recursive search inside the additional folders and return those tags as well. At that point, I need to only use the ‘name’ and ‘fullPath’ in my object to be used with the dropdown component.
Goal return format:

[
  {
    "fullPath": "[default]Customers/Demo/nampa_ls/Reads/WW_Level",
    "name": "WW_Level",
  },
  {
    "fullPath": "[default]Customers/Demo/nampa_ls/Reads/Pump 1/Running",
    "name": "Running",
  },
  {
    "fullPath": "[default]Customers/Demo/nampa_ls/Reads/Pump 1/Runtime",
    "name": "Runtime",
  },
  {
    "fullPath": "[default]Customers/Demo/nampa_ls/Reads/Pump 1/Start Count",
    "name": "Start Count",
  }
]

I appreciate any help!

value = '[default]test_folder'
browse_result = system.tag.browse(value, {}).getResults()
return [{'value': result['fullPath'], 'label': result['name']} for result in browse_result]

Nest a dictionary literal inside a list comprehension - so you’re looping through every element from getResults(), and for each one, construction a dictionary with value and label members (necessary for Perspective’s dropdown to work properly).

Thats exactly the format I need it in!
is there a way to incorporate a recursive browse into that?
I tried adding
if result['hasChildren'] == True: browseTags(result['fullPath'], filter)
But I keep getting errors.

I also tried creating a function to accomplish this, but that only returns the tag at the root and the two folders, it doesn’t do the recursive browse.
If I do a simple print instead of return, it does show me all the item, but still only returns the root tags.

def browseTags(path, filter):
    # Call the browse function
	results = system.tag.browse(path, filter).getResults()
    # Loop through every item in the results and print it
	for result in results:
		if result['hasChildren'] == False:
			print result
        # If the item has children, call the function to repeat the process but starting at the child.
		elif result['hasChildren'] == True:
			browseTags(result['fullPath'], filter)
		#return [{'value': result['fullPath'], 'label': result['name']} for result in results]
# Call the function
browse_results = browseTags('[default]Customers/Demo/nampa_ls/Reads', {})
print [{'value': result['fullPath'], 'label': result['name']} for result in browse_result]

Somewhat naive/optimistic implementation - be careful running this anywhere with lots and lots of tags. Basically just recurses the browseTags function as you had it, but adds the results to a continually growing list. The slice with no arguments ([:]) is a slightly underhanded Python way to copy a list in place.

def browseTags(path, browsefilter, current=[]):
    # Call the browse function
	results = system.tag.browse(path, browsefilter).getResults()

	# Loop through every item in a _copy_ of the current results (to avoid concurrently modifying the list)
	for result in results[:]:
		# no need for == True - a true value automatically passes the if check
		if result['hasChildren']:
			browseTags(result['fullPath'], browsefilter, current=results)
		else:
			results.append(result)
	else:
		return results

# Call the function
browse_results = browseTags('[default]', {})
print [{'value': result['fullPath'], 'label': result['name']} for result in browse_results]

That worked! Thanks for your help @PGriffith

It seems I spoke too soon, I used the wrong tag path in testing it.
It still only brings back the items at the base of the path, and it seems it now duplicates the tag that is not a folder.

[
{'label': u'Pump 2', 'value': [default]Customers/Demo/nampa_ls/Reads/Pump 2}, 
{'label': u'WW_Level', 'value': [default]Customers/Demo/nampa_ls/Reads/WW_Level}, 
{'label': u'Pump 1', 'value': [default]Customers/Demo/nampa_ls/Reads/Pump 1}, 
{'label': u'WW_Level', 'value': [default]Customers/Demo/nampa_ls/Reads/WW_Level}
]

I think I got it working with this:

def browseTags(path, browsefilter, current=[]):
    # Call the browse function
	results = system.tag.browse(path, browsefilter).getResults()
	tagList = []
	# Loop through every item in a _copy_ of the current results (to avoid concurrently modifying the list)
	for result in results[:]:
		# no need for == True - a true value automatically passes the if check
		if result['hasChildren']:
			recursive_results = browseTags(result['fullPath'], browsefilter, current=results)
			for result in recursive_results:
				tagList.append(result)
		else:
			tagList.append(result)
	return tagList

Yeah, that's probably what I missed. You could simplify that code a bit:

tagList.extend(browseTags(result['fullPath'], browsefilter, current=results))
1 Like

I think its due to the dictionary being inside a list, but trying to do any sort of manipulation on the label / value pairs is proving to be difficult.
To make it more human readable, and because browsing recursively adds a lot of duplicate name tags I am trying to use the ‘fullPath’ twice and splitting down the label string to only show the parent folder of the tag like this:

[
{'label': Pump 1/Running, 'value': [default]Customers/Demo/meridian_ls/Reads/Pump 1/Running}, 
{'label': Pump 1/Fault, 'value': [default]Customers/Demo/meridian_ls/Reads/Pump 1/Fault}, 
{'label': Pump 1/Runtime, 'value': [default]Customers/Demo/meridian_ls/Reads/Pump 1/Runtime}, 
{'label': Pump 1/Ignition_Minute, 'value': [default]Customers/Demo/meridian_ls/Reads/Pump 1/Ignition_Minute}, 
{'label': Pump 1/Ignition_Second, 'value': [default]Customers/Demo/meridian_ls/Reads/Pump 1/Ignition_Second}, 
{'label': Pump 1/Start_Count, 'value': [default]Customers/Demo/meridian_ls/Reads/Pump 1/Start_Count}
]

Is there a simple way of doing that?

One good option to remove duplication - replace the list structure that’s passed between executions with a dictionary - dictionaries are keyed and will automatically replace duplicate values, so if you key the entries off the full name you won’t end up with duplicates.
It will change the iteration you have to do at the end, though - you’ll need to loop through the dictionary’s items to pull out the tag objects, then put them into the list comprehension.

As for the splitting question:

fullpath = "[default]Customers/Demo/meridian_ls/Reads/Pump 1/Start_Count"
print "/".join(fullpath.split("/")[-2:])
>>> 'Pump 1/Start_Count'

explained:
fullpath.split("/") turns the string into a list of strings, “split” on the / character
[-2:] grabs from the second to last item (-2) to the end (nothing after the :)
"/".join() takes a sequence of strings and puts them together, using "/" as the delimiter - it’s an exact inverse operation to split()

I think I have it where it needs to be, thanks for the help @PGriffith