Creating a layers list for a stream (via Python)

hello, I’m back with another question!

I’m trying to use the Python API to update a stream by adding layers and objects, but I’m having trouble with the objects not updating as I’d expect. in Hestia, the stream name and layers all update, but the objects do not. if I go directly to the object url though, I’m able to see that the object value has been updated. I can also get the updated object value using another API call.

I’ve tried it both with a direct call and with the PySpeckle wrapper and the result is the same. this is my input:

# Format the parameters 
params={
    'name': 'Load Calc Results',
    'layers': [],
    'objects': [],
}
for room,load in load_results.items():
    params['layers'].append({'name': room})
    params['objects'].append({'type': 'Number', 'value': load})

# Update the stream
update = requests.put('https://hestia.speckle.works/api/v1/streams/{}'.format(out_stream), json = params, headers = headers)
print(update.json())

it runs fine and I get a success message {'success': True, 'message': 'Patched stream fields: name,layers,objects'} and calling the objects gives me the correct result. I guess it’s not a huge deal since when I ask for the data I’m getting what I want, but it would be great if I could work out how to get the values to update in Hestia so the other members of my team can see the results.

if anyone has any insight, I’d greatly appreciate any help!

python: 3.6.4
speckle: v1.2.6.110

Hmmm, so if i understand correctly, the UI in the frontend is not showing the updated numbers, right? If possible, can i have the streamId so i can look into debugging it (PM if you don’t want to share).

Might be that you’re not defining the layers correctly. I see you add them only with their name, but they need a couple of extra properties to work correctly, namely. Check the specs for the layer for the full info.

Off the top of my head, they will need the following extra props:

  • a guid of sorts
  • a startIndex value (integer), defined by the index of the first object in the stream’s object list that is on that layer
  • an objectCount, ie how many objects are on that layer - as determined by the stream’s object list.

Simple example (in js):

let objects = [ a, b, c, d, e, f ]
let firstLayer = { name: "First Layer", guid: "id of sorts", startIndex: 0, objectCount: 2 } // [ a, b ]
let secondLayer = { name: "Second Layer", guid:"use a lib to generate it", startIndex: 2, objectCount:3 } // [ c, d, e ]
let thirdLayer = { name: "Third Layer", guid:"use a lib to generate it", startIndex: 5, objectCount:1 } // [ f ]

You can also look at the logic to generate them in the frontend ui (the view is here and the state commits methods are here.

You might also need to add topology (i don’t remember if Grasshopper or Dynamo will crap out without it, in theory they shouldn’t).

yes, the frontend isn’t showing the numbers. the streamId is ZLw_1GkWS2.

thanks for the help! yeah I’m only defining the layers with a name; I didn’t realise the other fields were required. I’ll try that out now and see if I can fix it!

Yep, looking at it I’m sure that’s it.

If you have problems receiving it in grasshopper, then it might be the topology field. That’s easy to create actually if you don’t have nested data (trees, etc) - just define it as 0-${layer's object count}. Ie, { topology: "0-2" } if you have two objects on that layer.

just added the extra fields and it works!! thanks so much for the help :tada:

no worries, my pleasure! I’ll edit the name of this post to make it more relevant for future use - i’m sure this question will come again :smiley:

1 Like

here’s the full example in python in case anyone would like it for reference!

# Format the parameters 
params={
    'name': 'Load Calc Results',
    'description': 'This stream is updated from a python script `load-collector.py` on {}'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
),
    'layers': [],
    'objects': [],
}

for i, (room,load) in enumerate(load_results.items()):
    params['layers'].append({'name': room, 'guid': str(uuid.uuid4()), 'startIndex': i, 'objectCount': 1, 'topology': '0-1'})
    params['objects'].append({'type': 'Number', 'value': load})

# Update the stream
update = requests.put('https://hestia.speckle.works/api/v1/streams/{}'.format(out_stream), json = params, headers = headers)
print(update.json())
1 Like

@dimitrie is there something other than the topology that could be causing issues reading objects in Dynamo? I’m running into a problem where objects written to the stream in Python are coming through as null in Dynamo despite them showing up in the frontend UI.

Stream EBw4nQ7gD was manually entered by hand in the UI and Lbjn7PdIKf was pushed to the stream from Python. I’m defining the objects simply with 'name', 'type', and 'value' like this:

if lists_yes_no == 'yes':
        params['description'] = 'This stream was updated from `load-collector-v2.py` by {} on {} \n\n '.format(creds['name'],datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))
        for i,name in enumerate(data['room 1'].keys()):
            params['layers'].append({
                'name': name,
                'guid': str(uuid.uuid4()),
                'startIndex': i*len(room_data),
                'objectCount': len(room_data),
                'topology': '0-{}'.format(len(room_data)),
            })
            for room,load in data.items():
                params['objects'].append({'name': name, 'type': 'Number', 'value': load[name]})

I can’t really seem to find a difference between an object from the manual stream and an object from the python stream as it looks like it generates its own _id and hash and the other fields are similarly empty/default in the working objects. I must be missing something, but I can’t quite work it out. any help on this would be fab!

Can you check if they show up in grasshopper too? I’m away from my keyboard at the moment :zipper_mouth_face:

no prob! the Python stream updates the stream name but is stuck on getting the objects (doesn’t stop loading)

This sounds stupid, but try adding an empty space at the end of the topology string for each layer. I suspect/smell some bad string splitting on the client side… (off the top of my head, can debug around lunchtime)

haha just tried it but unfortunately it didn’t work :cry:

sure thing, let me know what you find! my code is here if you need to take a look or reproduce the issue.

appreciate all your help!

Okay, got it! You’ll need to provide the orderIndex property on each layer, gh is dependent on it. I’ve made a pr with a fix to this, but in the meantime add a orderIndex prop to your layers - start at 0 for the first one and increment by one as you go :sunglasses:

Proof it will work:

amazing; thanks so much for all your help!! :sparkles:

1 Like