How to fix error Dataset Utilities $PyDataSet$PyRow indices must be integers?

New to Ignition. Some help to fix this please. I have this Table Script in which line 13: table_data[i]["qtyKitted"] = current_qty_kitted + 1 , gives the error DatasetUtilities$PyDataSet$PyRow indices must be integers.

Here is the script:


def transform(self, value, quality, timestamp):
    table_data = system.dataset.toPyDataSet(value)  # Convert dataset
    # Current number of parts to pick
    cur_val = self.getSibling("NumericQtyPicked").props.value
    # Iterate through each row in the table to distribute parts picked to each truck
    for i in range(table_data.getRowCount()):
        if table_data.getValueAt(i, "itemNo") == self.getSibling("LabPartNumber").props.text:
            # Distribute currentValue to qtyKitted not exceeding PEG_QTY
            while cur_val > 0 and table_data.getValueAt(i, "qtyKitted") < table_data.getValueAt(i, "PEG_QTY"):
                current_qty_kitted = table_data.getValueAt(i, "qtyKitted")
                if current_qty_kitted < table_data.getValueAt(i, "PEG_QTY"):
                    # Update value one at at time
                    table_data[i]["qtyKitted"] = current_qty_kitted + 1
                    cur_val -= 1
    # Convert back to dataset
    value = system.dataset.toDataSet(table_data)
    self.view.params.SelectedItems = value
    return value

Thank you!!!

You are trying to assign back into a dataset/PyDataset. While there are ways to do it, you aren't supposed to. Make a new dataset.

4 Likes

There is a better way to write this script, which will make it more readable, more performant, and not have the error you're seeing.

This was written with only knowledge of the script you provided and so some assumptions were made about the shape of your data.

I have left some comments in it to help explain what's going on a bit.

def transform(self, value, quality, timestamp):
	
	#convert value to a pyDataSet
	table_data = system.dataset.toPyDataSet(value)
	
	cur_val = self.getSibling("NumbericQtyPicked").props.value
	#this is constant in the loop so no need to retrive it more than once.
	lab_part = self.getSibling("LabPartNumber").props.text
	
	#The main advantage of PyDatasets is that they allow you to use them in more pythonic ways
	#anytime you write for something in range() there is probably a better way.
	
	#create a variable to hold the qtyKitted
	qty_kitted = 0
	lab_part_row = 0

	#enumerate will return both the actual row data and an iterated integer value.
	#This takes advantage of a pydatasets iterable behavior while allowing us to
	#also keep track of what row we find the part on.
	for i,row in enumerate(table_data):
		if row['itmeNo'] == lab_part
			#Distribute currentValue to qtyKitted not exceeding PEG_QTY
			#There is no need to loop here. What you're really trying to do
			#is either setting the qty kitted to the current_val or the "PEG_QTY" if
			#the current value is >= "PEG_QTY"
			
			qty_kitted = table_data["qtyKitted"]
			peg_qty = table_data["PEG_QTY"]
			qty_kitted += cur_val if cur_val < peg_qty else peg_qty
			
			
			#now that we've found the lab_part there is no need to go further
			#save the lab_part_row and exit the loop
			lab_part_row = i
			break
			
	#create a new dataset from the old one with the qtyKitted updated to the new value
	newDataset = system.dataset.toDataset(system.dataset.setValue(value, lab_part_row, "qtyKitted", qtyKitted)
	self.view.params.SelectedItems = newDataset
	return newDataset
3 Likes

That looks super weird to me.
If that transform is in the binding on SelectedItems, you don't need to assign the dataset to the property, it will get the value returned by the script.
If it's on another property, don't assign it there, make another binding.

2 Likes

Thank you so much for taking the time to dissect your answer. I will optimize my routine and post it later. The internal loop is necessary because it manages sets of parts inside of other sets of parts. Your answer pointed me in the right direction. Thanks again!

Not a problem.

The internal loop is never resetting cur_val so, once you have exhausted that nothing will change.

You can just as easily remove the number of parts from cur_val and achieve exactly the same result without a loop.

1 Like

I am sure it is weird, as I mentioned I am new to ignition.
This routine controls the current table and a dataset for another table, hence the reason I placed the resulting data both in self.view.params.SelectedItems and the current table. Subsequently both table/dataset are filtered further with distinctive criteria.
Peculiar solution maybe but it works the way I need it. Thank you for your time!

It works, but it's not good practice.

As Pascal says, remove this from your script

self.view.params.SelectedItems = value

Then bind the view param SelectedItems to the table data. It will make it much easier later if you (or someone who inherits your project) needs to troubleshoot and determine where the data for that parameter comes from.

2 Likes

Good point. I changed it. Thank you for the advice!