Get Project Edit History in Gateway Script

This feels like it ought to be a fairly trivial task, but has proven to be more difficult than I thought.

I’m able to get an array of RuntimeProject from IgnitionGateway.get().getProjectManager() context. But none of the methods in the project object, like the manifest or snapshot objects, have the info I’m looking for.

Does anyone know of any way to get at edit details of a given project besides internal db queries? All I really need is the edit count, edit author, and edit timestamp.

The concept of an edit count doesn’t exist any more.

On a per-resource basis you can try to get the LastModification attribute by calling ProjectResource::getAttribute with “lastModification” as the key.

On a per-resource basis you can try to get the LastModification attribute by calling ProjectResource::getAttribute with “lastModification” as the key.

Thanks, this is helpful, but I'm really after more of a project-wide LastModification value. Is there a way to get this besides iterating over getProject(projectName).get().getAllResources() and returning the max LastModification value?

No, that’s probably your best bet.

Not sure what guarantees you need from this, but the LastModification details are part of the resource and disk and can be freely modified by anybody and if e.g. you are using git for managing resources the file system date and the date indicated by LastModification could be pretty different.

So, the “author” and “modified” values in this screenshot from the gateway perspective session launcher - where does this info come from?
image
This is essentially what I’m after…

It’s doing exactly what you suggested - taking the LastModification entry with the most recent timestamp and using the actor and timestamp from that.

Looks like I marked this one as solved a hair too quickly…

Here is what I have so far to attempt to get the LastModification attribute:

	import json
	## get gateway context to grab project manager
	from com.inductiveautomation.ignition.gateway import IgnitionGateway
	gw = IgnitionGateway.get()
	project_manager = gw.getProjectManager()
	project_names = [name for name in project_manager.getProjectNames()]
	project_manifests = []
	projects = [project_manager.getProject(name).get() for name in project_names]	## Need to get() from the java "Optional" object
	for project in projects:
		manifest = project.getManifest()
		jsonManifest = json.loads(manifest.toJson(manifest))
		project_manifests.append(jsonManifest)
		## get all resources and loop over them to find the latest timestamp from 'LastModification' attribute
		resources = project.getAllResources()
		latest = None	## update this timestamp whenever a more recent timestamp is encountered.
		for resource in resources:
			if resource.getProjectName() == project.getName():
				return({'json': resource})	## this returns an object with projectName, resourcePath, and resourceType attributes
				return({'json': dir(resource)})	## this returns a list that does not include 'getAttribute' or 'getAttributes'..
				return({'json': resource.getAttributes()})	## this also throws attribute error
				lastModification = resource.getAttribute('LastModification')	## this throws attribute error 
	d = {'project_names': project_names, 'project_manifests': project_manifests}
	return({'json': json.dumps(d)})
	

This is in a python doPost web dev resource. Gateway is running 8.1.10. You can see that I have debug return statements in the code to inspect the resource object in that last loop. The dir(resource) almost returns the same list that I would expect of the ProjectResource object, but it is missing the getAttribute and getAttributes methods I need.

What’s the output from the dir line? (assuming you comment the other returns out)

I do kind of remember Jython having issues with default methods on Java interfaces, which would maybe explain getAttribute failing, but not getAttributes

Oh nevermind I see what you’re doing wrong.

getAllResources returns a map of ProjectResourceId to ProjectResource. You are iterating over the keys, not the values.

1 Like

You are iterating over the keys, not the values.

D'oh!!! Yep, you're right. The reason I didn't catch that sooner, is that the return from the first debug statement return({'json': resource}) didn't look like a key:

{u'projectName': u'v7_staging_tool_backup',
 u'resourcePath': {u'path': {u'parts': [u'Main Window (1)']},
                   u'resourceType': {u'moduleId': u'com.inductiveautomation.vision',
                                     u'typeId': u'windows'}}}

What’s the output from the dir line?

And this looked close enough to what I was expecting as well. Output of dir(resource) (with private attr's hidden):

[u'class',
 u'equals',
 u'folderPath',
 u'fromJson',
 u'getClass',
 u'getFolderPath',
 u'getProjectName',
 u'getResourcePath',
 u'getResourceType',
 u'hashCode',
 u'notify',
 u'notifyAll',
 u'projectName',
 u'resourcePath',
 u'resourceType',
 u'toJson',
 u'toString',
 u'wait']

In any case, changing for resource in resources: to for resourceId, resource in resources.items(): fixed my problem, and I am able to get the lastModification attribute now. Thanks for pointing me in the right direction on my iterator!

For posterity, here is the code from my web dev resource that allows me to get an array of dicts, one for each project on a gateway. For each project, I get the following info: (basically name, manifest data, last modification)

 {u'actor': u'<dev user name>',
  u'description': u'<project description>',
  u'enabled': True,
  u'inheritable': False,
  u'name': u'<project name>',
  u'parent': u'global',
  u'timestamp': u'2022-01-28T20:44:03Z',
  u'title': u'<project title'}

Web dev code:

	import json
	## get gateway context to grab project manager
	from com.inductiveautomation.ignition.gateway import IgnitionGateway
	gw = IgnitionGateway.get()
	project_manager = gw.getProjectManager()
	projects = [project_manager.getProject(name).get() for name in project_manager.getProjectNames()]	## Need to get() the project from the java "Optional" object
	projectDicts = []
	for project in projects:
		d = {'name': project.getName()}	## build the rest of the dict below
		manifest = project.getManifest()
		jsonManifest = json.loads(manifest.toJson(manifest))
		d.update(jsonManifest)	## add json manifest map to dictionary
		## get all resources and loop over them to find the latest timestamp from 'LastModification' attribute
		resources = project.getAllResources()
		actor, timestamp = (None, None)	## update this timestamp whenever a more recent timestamp is encountered.
		for resourceId, resource in resources.items():
			if resource.getProjectName() == project.getName():		## need to add this filter to ignore resources from inherited parent projects
				try:
					lastModification = dict(resource.getAttribute('lastModification').get())
					ts, a = (lastModification['timestamp'], lastModification['actor'])
					if timestamp is None or ts > timestamp:
						timestamp, actor = (ts, a)
				except:	continue
		d['actor'], d['timestamp'] = (actor, timestamp)
		projectDicts.append(d)
	d = {'projects': projectDicts}
	return({'json': json.dumps(d)})
	
2 Likes