Errors in script to find orphaned tags and recursion length

I am trying to build a script to find orphaned tags, and am getting errors relating to QualifiedPath and cannot coerce value "histprov:prov_name" into type class java.lang.string.

This is the code I have at the moment:

from java.util import Date

# Parameters
days_without_data = 30  # Threshold for inactivity
cutoff_time = system.date.addDays(system.date.now(), -days_without_data)

def collectHistoricalTagPaths(provider, parentPath=""):
    tag_paths = []
    results = system.tag.browseHistoricalTags(provider, parentPath).getResults()
    for result in results:
        if result.hasChildren():
            tag_paths.extend(collectHistoricalTagPaths(provider, result.getPath()))
        else:
			tag_path = "[{}]{}".format(provider, result.getPath())
			tag_paths.append(tag_path)
    return tag_paths
    
orphaned_tags = []
providers = system.tag.browseHistoricalTags()
provider_results = providers.getResults()


for provider_result in provider_results:
    # Convert QualifiedPath to string and extract provider name
    full_path_str = str(provider_result.getPath())  # e.g., 'histprov:prov_name:/drv:...'
    if full_path_str.startswith("histprov:"):
        provider_name = full_path_str.split(":")[1].split("/")[0]  # Extract 'prov_name'
        tag_paths = collectHistoricalTagPaths(provider_name)
        for tag_path in tag_paths:
            try:
                history = system.tag.queryTagHistory(
                    paths=[tag_path],  # Must be a list of strings
                    startDate=cutoff_time,
                    endDate=system.date.now(),
                    returnSize=1,
                    aggregationMode="LastValue"
                )
                if history.getRowCount() == 0:
                    orphaned_tags.append(tag_path)
            except Exception as e:
                orphaned_tags.append(tag_path)

Which throws the java.lang.ClassCastException error for coercing the value into a string, specifically on the line that calls the function: collectHistoricalTagPaths.

I know that the provider_name that is passed into the function is simply the name of the historical provider. One example from the list is:Sample_SQLite_Database.

I think the Exception is actually thrown on the line:
tag_paths.extend(collectHistoricalTagPaths(provider, result.getPath()))

You're passing the raw underlying Java type in to the recursive execution of collectHistoricalTagPaths - which then gets passed to browseHistoricalTags, but that function only accepts a real string, at this line:
results = system.tag.browseHistoricalTags(provider, parentPath).getResults()

The simplest fix would be to ensure the path is a string before you start your recursion:
tag_paths.extend(collectHistoricalTagPaths(provider, str(result.getPath())))

Aha! I was looking at the wrong parameter. Fixed that, thank you. Now I get a maximum recursion length error :joy:

I added a maximum depth to the recursion:

days_without_data = 30  # Threshold for inactivity
cutoff_time = system.date.addDays(system.date.now(), - days_without_data)
max_depth = 4

def collectHistoricalTagPaths(provider, parentPath="", depth=0):
	count = 0
	tag_paths = []
	if depth > max_depth:
		return tag_paths
		
	results = system.tag.browseHistoricalTags(provider, parentPath).getResults()
	for result in results:
#		count += 1
#		print(count)
		child_path = str(result.getPath())
		if result.hasChildren():
			tag_paths.extend(collectHistoricalTagPaths(provider, child_path, depth + 1))
		else:
			tag_path = "[{}]{}".format(provider, child_path)
			tag_paths.append(tag_path)
	
	return tag_paths

If max_depth is 5, it seems to go on forever. At 1, 2 and 3, I get no results for orphaned tags. currently running with depth at 4, which seems to take about 1 minute per Historical Tag Provider. TBH, I am not even sure I am asking the system.tag.browse... the correct question.

Well... No results with a depth of 4. Hm... Maybe there are no orphaned tags? Probably have to poke around the DB tables or something.

If you’re running into recursion depth errors, you can drop the recursion and maintain a stack instead.
Start by adding the root to the stack, then loop on while stack so you’re processing items as long as the stack is not empty.

In that loop, pop the stack, process the item, and put its children on the stack.

This pretty much does what recursion does, but without its issues.

PS: I didn’t read your code, I’m on mobile and I dislike reading code on mobile.

I just select tag paths from sqlth_te where retired is null into a list.

Then chunk reading historyEnabled with a system.tag.readBlocking(tpaths)

Then you can just filter or traverse the results looking for tags with historyEnabled False.

Build up a list of those and after that I just select top 1 retired from the partition and write it to the retired field of the False tags.

What do you mean?

To set the orphaned tags as retired, you can just select the top retired value from the table and set the tags record you want to retire to that value.

Or you can get the timestamp from the system and use that.

I just script it all on buttons with tables on a window.

Run it every few months just to check on things.

Especially on DEV gateways where we are moving thousands of tags and setting/resetting history enabled on them.

SELECT TOP (1) [retired] FROM [sqlth_te] ORDER BY [retired] DESC

Ah, ok. Just wanted to make sure I understood.

So, I am going through the list of Historical Tag Providers using the .browseHistoricalTags() and for many of them I get Unknown Provider Name. Why would this be?

EDIT
Also, while playing around with system.tag.browse, I am getting no results. Our Tag Provider name is default, so it seems that this: results = system.tag.browse('[default]').getResults() should give me something?? But I get a zero length list.

Where is there a way to read historyEnabled? I haven't found anything in the docs about filtering on this property, yet.

You can read ~any property of a tag by just concatenating the 'raw' name of the property with the path.

That is:

  • system.tag.readBlocking(["path/to/tag"]) -> a QualifiedValue containing the tag's value
  • system.tag.readBlocking(["path/to/tag.value"]) -> a QualifiedValue containing the tag's value
  • system.tag.readBlocking(["path/to/tag.someOtherProperty"]) -> a QualifiedValue containing someOtherProperty's value on the tag
  • system.tag.readBlocking(["path/to/tag.historyEnabled"]) -> a QualifiedValue containing the value of the historyEnabled property
1 Like