Python issue replacing an empty key

Hello to all! Python novice here back with another problem… :frowning:

Here’s the script:

data = jsonDict['data'][0]
operDict = jsonDict['data'][0]['operations'] 
rows = []
FMT = '%Y-%m-%d %H:%M:%S' 
for row in operDict:
	newrow = []
	newrow.append(row['fwork-code'])
	newrow.append(row['fwork-id'])
	newrow.append(row['fwork-dscr'])
	newrow.append(row['from']) 
	newrow.append(row['to'])
	d1 = row['from']
	d2 = row['to']
	newrow.append(datetime.strptime(d2, FMT) - datetime.strptime(d1, FMT))
	newrow.append(round(row['actualtime']/3600.0,2))
	newrow.append(row['status'])
	rows.append([str(r) for r in newrow])

headers = ['OP Code', 'ID', 'Description', 'Start', 'End', 'Elapsed', 'Labor Hrs', 'Status']
dataset = system.dataset.toDataSet(headers, rows)
event.source.parent.getComponent('Oper_Table').data = dataset

It works perfectly, right up until it hits an empty “from” or “to”

I’ve tried ‘if else’, ‘try and except’, and just can’t get past the error. Here’s my try and except:


	newrow.append(row['fwork-dscr'])
	try:
		newrow.append(row['from']) 
	except:
		datetime.strptime(datetime.now(),FMT)
	try:
		newrow.append(row['to'])
	except:
		datetime.strptime(datetime.now(),FMT)

But I still get this error:

Traceback (most recent call last):
  File "<event:actionPerformed>", line 33, in <module>
  File "C:\Users\rforshey\.ignition\cache\gwhaycic_8088_443_main\C0\pylib\datetime.py", line 1499, in strptime
    return cls(*(_time.strptime(date_string, format))[0:6])
ValueError: time data did not match format:  data=  fmt=%Y-%m-%d %H:%M:%S

Any ideas from the Python experts out there?

Thanks!!

A technique like this should work:

if row['from']: # row value is 'truthy', ie, not an empty string or null value (None)
	d1 = datetime.strptime(row['from'], FMT)
else:
	d1 = datetime.strptime(system.date.now(), FMT)
newrow.append(d1)

Although, that still won’t protect you from a value being present that isn’t a valid datestring for your format specifier - to catch that, wrap it all in a try/except:

try:
	if row['from']: # row value is 'truthy', ie, not an empty string or null value (None)
		d1 = datetime.strptime(row['from'], FMT)
	else:
		d1 = datetime.strptime(system.date.now(), FMT)
except ValueError:
	d1 = "Invalid date"
newrow.append(d1)

Also, not a big deal, but if you’re posting code on the forums it’s best to put it inside triple backticks so formatting isn’t lost:

```
code

```
1 Like

Also datetime.now() doesn’t return a string,

from datetime import datetime
FMT = '%Y-%m-%d %H:%M:%S' 
print datetime.now()
datetime.strptime(str(datetime.now()),FMT)

If you check the type of datetime it’s <class ‘datetime.datetime’>

1 Like

Oh, yeah, I didn’t even notice you were using datetime objects. For general sanity/interoperability reasons, you’re probably better off using our system.date functions, rather than the datetime library for Python.

3 Likes

You guys are awesome, thanks for the help!

A follow on question:

The Dictionary has basically 3 levels and using the strategy given before, I’m able to get all of the rows from the second and third levels. But I am not able to use the script for the first level. I’ve come up with a temporary work around (Select 1, 2 3) but would rather not use this in the long run. As you can see in the below script, I’m getting the data from “data” and operDict. I’ve tried setting up the “data” portion of the script with the newrow type stuff from later in the script but it fails. Any ideas?

from urllib import urlencode
from datetime import datetime
FMT = '%Y-%m-%d %H:%M:%S'
if event.source.parent.getComponent('Select 1').selected:
	x = 0
if event.source.parent.getComponent('Select 2').selected:
	x = 1
if event.source.parent.getComponent('Select 3').selected:
	x = 2	
#Salord = event.source.parent.getComponent('Work_Order').selectedStringValue
Salord = event.source.parent.getComponent('WO_List').selectedStringValue
args = {"sales-order": Salord, "token": "cfapiKnJ4Td2WQ8hf"}
Server = event.source.parent.getComponent('Table_Select').Server
response = system.net.httpGet("http://" + Server + ":8080/cellfusion/api/v1/orders/details?" + urlencode(args))

#stores jsonString into a python object/dict
jsonDict = system.util.jsonDecode(response)

#Assigns the variable, data, to the dictionary inside of jsonDict
data = jsonDict['data'][x]
operDict = jsonDict['data'][x]['operations'] 

id = str(data['id'])
mfgstatusCode = str(data['mfgstatus-code'])
assemblyid = str(data['assembly-id'])
assemblyCode = str(data['assembly-code'])
qty = str(data['mfgqty'])
description = str(data['assembly-dscr'])
workorder = str(data['salord'])
status = str(data['mfgstatus-dscr'])
opsopen = str(data['opsopen'])
opsclosed = str(data['opsclosed'])
currentop = str(data['currentop-code'])
laborhrs = str(round(data['actualtime']/3600.0,2))

headers = ['ID', 'Status Code', 'Assembly ID', 'Top Level PN', 'Quantity', 'Description', 'Work Order', 'Status', 'Ops Open', 'Ops Closed', 'Current Op', 'Labor Hrs' ]
rows = [
[ id, mfgstatusCode, assemblyid, assemblyCode, qty, description, workorder, status, opsopen, opsclosed, currentop, laborhrs ] ,  
]

dataset = system.dataset.toDataSet(headers, rows)
event.source.parent.getComponent('Head_Table').data = dataset

# create holder list
rows = []
 
for row in operDict:
	newrow = []
	newrow.append(row['fwork-code'])
	newrow.append(row['fwork-id'])
	newrow.append(row['fwork-dscr'])
	newrow.append(row['from']) 
	newrow.append(row['to'])
	try:
		if row['from']:
			d1 = row['from']
		else:
			d1 = "No Date"
	except ValueError:
		d1 = "No Date"
	try:
		if row['to']:
			d2 = row['to']
		else:
			d2 = "No Date"
	except ValueError:
		d2 = "No Date"
	try:
		if row['from']:
			newrow.append(datetime.strptime(d2, FMT) - datetime.strptime(d1, FMT))
		else: 
			newrow.append('0')
	except ValueError:
		"No Value"
	try: 
		if row['actualtime']:
			newrow.append(round(row['actualtime']/3600.0,2))
		else:
			newrow.append('0')
	except ValueError:
		'0'
	newrow.append(row['status'])

# add our new row (casting each element to a string) to the overall rows list
	rows.append([str(r) for r in newrow])

headers = ['OP Code', 'ID', 'Description', 'Start', 'End', 'Elapsed', 'Labor Hrs', 'Status']
dataset = system.dataset.toDataSet(headers, rows)
event.source.parent.getComponent('Oper_Table').data = dataset

can you snippet the json structure you’re working with?

Unless the structure is different between 1,2, and 3- it shouldn’t matter.

“id”: “31138”,
“code”: “31138”,
“salord”: “001000032409”,
“from”: “2019-10-08 15:22:21.0”,
“to”: “”,
“mfgstatus-code”: “0003”,
“mfgstatus-dscr”: “In process”,
“mfgqty”: 1,
“assembly-id”: “3233”,
“assembly-code”: “NEW-VXT”,
“assembly-dscr”: “GENERIC TEMPLATE FOR VXT GAS BOXES”,
“opsopen”: 5,
“opsclosed”: 2,
“currentop-id”: “37446”,
“currentop-code”: “0030”,
“currentop-dscr”: “ENCLOSURE ASSEMBLY”,
“currentevent-id”: “50808”,
“currentevent-code”: “0300”,
“currentevent-dscr”: “VERIFICATION BEFORE FUNCTIONAL TEST”,
“actualtime”: 179707,
“operations”: [
{
“fwork-id”: “36824”,
“fwork-code”: “0010”,
“fwork-dscr”: “MAIN ASSEMBLY”,
“from”: “2019-10-08 15:22:22.0”,
“to”: “2019-10-12 14:19:16.0”,
“actualtime”: 9395,
“status”: “Closed”,
“events”: [
{
“fwork-id”: “50784”,
“fwork-code”: “CRADLE/WELD ASSY”,
“fwork-dscr”: “CRADLE/WELD ASSY”,
“start”: “2019-10-08 15:22:22.0”,
“end”: “2019-10-12 14:19:16.0”,
“actualtime”: 116,
“status”: “Closed”

Here’s a bit of result. As you can see, there are three dictionarys: Data, Operations, and Events. I can iterate through Operations and Events, but not through Data. In a given query, there are multiple data(ID), Operations(fwork-id) and Events(fwork-id). I’m able to iterate and pull all of the rows for the Operations and Events, but get the “unicode indices must be integers” error when trying to iterate through the data dictionary. Any ideas?

Not sure if it’s a typo in the example, but there is no “Data” key in the dictionary.

If there’s a possibility a key may not exist because it’s empty.

A couple of things you can try is adding a check if the key exists:

if "Data" in dictionary.keys():

Another option is to use the “get” function:

dictionary.get("Data",[])

this tells the dictionary to retrieve the object in Data, or return an empty list if the key is not found (ie. default value).

This way if you attempt to iterate a non-existent key it will iterate an empty list and just return nothing back.