User triggers onCellEdited listener multiple times on a single edit

Hello.

In one of our tables the user can edit its contents. When they do, the changes get written to a log stored in the database through the onCellEdited Power Table extension function.
Today a user brought up that when he edits a table the log states he made between 9-12 edits, all less then 1 second apart.
I looked into it, there are no loops he could somehow get trapped in. I cant reproduce the issue, and the 4 other users I asked could not repeat the issue.

Our Ignition version is 7.9.10, his Java version is Version 8 Update 191 (same as mine).

I’ve bandaged the issue by adding a locking variable to the table. Essentially if the variable is true (locked) the onCellEdited function skips everything and does nothing. If its false (unlocked) it can do what it should. So when it triggers 9-12 times, only the first locks it and does anything.

However this will likely be a problem with other tables in our system. Is this a known bug, or an issue that that has come up before? Is there a reason an onCellEdited listener would trigger multiple times for a certain user?

As requested, code below

# Gathering relevant data for log
data = system.dataset.toPyDataSet(self.data)
ticket_info = system.db.runPrepQuery("select * from TABLE1 where event_id = ?",[self.log])
owner = ticket_info[0]['nw_owner']
evid = self.log
subj = ticket_info[0]['subject']
site = ticket_info[0]['site']
details = ticket_info[0]['desc']
nsid = ticket_info[0]['netsuiteid']
eventdate = ticket_info[0]['eventdate']
callername = ticket_info[0]['callername']
email = ticket_info[0]['calleremail']
issuetype = ticket_info[0]['issue_type']
priority = data[rowIndex]['priority']
stat = data[rowIndex]['callstatus']
content = ""
user = system.security.getUsername()


def updateNotes(field):
"""
Updates log in the database

Args:
	field (str): Field being updated
"""

	field = field.replace('_',' ')
	parts = field.split(' ')
	i = 0
	for part in parts:
		newpart = part.replace(part[0],part[0].upper())
		parts[i] = newpart
		i+=1
		
	field = ''
	
	for part in parts:
		field += part + ' '
	
	field = field.rstrip()
	
	#add note to ticket to show case is created
	#first query the notes for the event
	notes = system.db.runPrepQuery("select notes from TABLE1 where event_id = ?",[evid])
	note = notes[0][0]
	
	if note is None:
		note = ''
	#update notes
	note += "\n\nTicket Update by %s at %s"%(user,system.date.format(system.date.now(),'EEE, MMM d yyyy - HH:mm'))
	note += "\n%s changed to %s from %s" %(field,newValue,oldValue)
	#update database
	system.db.runPrepUpdate("update TABLE1 set notes = ? where event_id = ?",[note,evid])

if colName == 'callstatus':
	
	query = 'update TABLE1 set `%s` = "%s" where event_id = %d' % (colName,newValue,evid)
	
	#if the status has changed to needs dispatch, set a servicedate of today		
	if newValue == 'Needs Dispatch':
	
		system.db.runPrepUpdate("update TABLE1 set servicestartdate = ? where event_id = ?",[system.date.format(system.date.now(),'yyyy-MM-dd HH:mm:ss'),evid])
	
	
	updateNotes('Ticket Status')
	
	if user != owner:
		message = "Ticket %d (%s) for %s status has changed from %s to %s by %s.  " %(evid,subj,site,oldValue,newValue,user)		
		result = shared.telegram.sendTicketBotMessage(message,owner)
				
	
	#if ticket is complete, auto popup the email editor
	if newValue == 'Complete':
		
		stat = 'Complete'
		
		if callername is None:
			callername = 'Dear Valued Customer'
		
		if issuetype is None:
			issuetype = 'Other'
		
		body = callername + ','
		body += "\n\nStatus Update on Ticket: %d" %evid
		body += "\n\nLocation: %s" %site
		body += "\nEvent Date and Time: %s" %system.date.format(eventdate,'yyyy-MM-dd HH:mm')
		body += "\nIssue Type: %s" %issuetype
		body += "\n\nDetails: \n%s" %details
		body += "\n\nCurrent Status: %s"%stat
		body += "\n\nAdditional Notes from  Operator:\n\n"
		
		win = system.nav.openWindow('Popups/Email Details Default Client',{'to':email,'cc':'','subject':subj,'body':body,'nsid':nsid,'event_id':evid})
		system.nav.centerWindow(win)
		

else:
	query = 'update TABLE1 set `%s` = "%s" where event_id = %d' % (colName,newValue,evid)
	updateNotes(colName)


system.db.runUpdateQuery(query)
self.data = system.dataset.setValue(self.data, rowIndex, colIndex, newValue)

I’ve not heard of any such problem. Please show your code.

Added to the post, please let me know if theres more you need.

Is the data property of your table bound to a query? If so, consider not setting the data property at the end of your script, but instead use system.db.refresh(). Also, don’t use a query to collect the current row data – get it right from the current dataset. Also consider using invokeAsynchronous to handle your actual update queries.

Thanks, I’ll give these a try and see if the user has the same problem. What still bothers me however is that this is only a problem for this one guy, but this code has worked flawlessly for everyone else for the past 6+ months

Consider designing such that event code runs really fast – milliseconds – without calling out for data not in scope. Callouts can have weird timing dependencies on a machine by machine basis that yield race conditions. Also keep in mind that setting properties on an object within an event script will call any propertyChange scripts that apply, nesting events. This is ok as long as everything runs fast and doesn’t recurse back to the original event. If you have a propertyChange on the table’s data property, it’ll nest in your onCellEdited event.

Thank you for your help