# Get Project Edit History in Gateway Script

**URL:** <https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497>\
**Category:** Ignition\
**Tags:** ignition81, scripting\
**Created:** [January 28, 2022, 6:14pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497 "2022-01-28T18:14:36Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![dustin.cooper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dustin.cooper/32/29963_2.png) [@dustin.cooper](https://forum.inductiveautomation.com/u/dustin.cooper)\
**Post date:** [January 28, 2022, 6:14pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/1 "2022-01-28T18:14:36Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [January 28, 2022, 6:44pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/2 "2022-01-28T18:44:46Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![dustin.cooper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dustin.cooper/32/29963_2.png) [@dustin.cooper](https://forum.inductiveautomation.com/u/dustin.cooper)\
**Post date:** [January 28, 2022, 7:22pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/3 "2022-01-28T19:22:19Z")

</div>

> 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?

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [January 28, 2022, 7:25pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/4 "2022-01-28T19:25:02Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![dustin.cooper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dustin.cooper/32/29963_2.png) [@dustin.cooper](https://forum.inductiveautomation.com/u/dustin.cooper)\
**Post date:** [January 28, 2022, 7:30pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/5 "2022-01-28T19:30:10Z")

</div>

So, the “author” and “modified” values in this screenshot from the gateway perspective session launcher - where does this info come from?  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/4/04976fbe747d5e17bab402e8db8ff1d4687e8fe5.png)  
This is essentially what I’m after…

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [January 28, 2022, 7:32pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/6 "2022-01-28T19:32:22Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![dustin.cooper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dustin.cooper/32/29963_2.png) [@dustin.cooper](https://forum.inductiveautomation.com/u/dustin.cooper)\
**Post date:** [January 28, 2022, 8:46pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/7 "2022-01-28T20:46:29Z")

</div>

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:

```auto
	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](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.11/com/inductiveautomation/ignition/common/project/resource/ProjectResource.html) object, but it is missing the `getAttribute` and `getAttributes` methods I need.

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [January 28, 2022, 8:52pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/8 "2022-01-28T20:52:13Z")

</div>

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`…

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [January 28, 2022, 8:59pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/9 "2022-01-28T20:59:54Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![dustin.cooper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dustin.cooper/32/29963_2.png) [@dustin.cooper](https://forum.inductiveautomation.com/u/dustin.cooper)\
**Post date:** [January 28, 2022, 9:19pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/10 "2022-01-28T21:19:05Z")

</div>

> 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:

```auto
{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):

```auto
[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!

---

<div class="post-metadata">

**Author:** ![dustin.cooper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dustin.cooper/32/29963_2.png) [@dustin.cooper](https://forum.inductiveautomation.com/u/dustin.cooper)\
**Post date:** [January 28, 2022, 10:19pm UTC](https://forum.inductiveautomation.com/t/get-project-edit-history-in-gateway-script/55497/11 "2022-01-28T22:19:20Z")

</div>

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)

```auto
 {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:

```auto
	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)})
	

```
