Hey @ZStructural, a bit more on the Python retrieval of the Levels data. The GraphQL API is your friend here. In the context of Speckle, you can retrieve exactly the data you need in a single request, reducing the amount of data transferred over the network and speeding up your application.
Here, we can add a GQL client directly:
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
Before we proceed, let’s initialize some variables. The commit_referenced_object
is an identifier for the specific data set you’re interested in, which you would have obtained when you retrieved the commit object. The HOST_SERVER
is the URL of the Speckle server from which you are querying data. Make sure both of these variables are set before running the code.
commit_referenced_object = commit.referencedObject
gql_client = Client(
transport=RequestsHTTPTransport( url=f"{HOST_SERVER}/graphql" )
)
The GraphQL query is designed to fetch specific data from the server. Here, we are interested in a stream
identified by stream_id
, and within that, an object
identified by commit_referenced_object
. The limit: 1000
specifies that we want to retrieve a maximum of 1000 records. The select
array outlines the data fields we want to fetch: the level names and their elevations. 100 used is arbitrarily large, the default is lower than this, which means you may not capture all the levels as you haven’t parsed all the commit objects.
query = gql(
"""
query Commit( $stream_id: String!, $commit_referenced_object: String! ) {
stream(id: $stream_id) {
object(id: $commit_referenced_object) {
children( limit:1000,
select: [
"level.name",
"level.parameters.LEVEL_ELEV.value"
]
) { objects { data } } } } } """
)
params = {
"stream_id": stream_id,
"commit_referenced_object": commit_referenced_object
}
Once the query and parameters are set, the gql_client.execute()
function sends the query to the server and fetches the data. The received data is stored in the received_data
variable for further processing.
received_data = gql_client.execute(query, variable_values=params)
Now if you inspect received_data
, you’ll see something like:
{
"stream": {
"object": {
"children": {
"objects": [
{
"data": {
"level": {
"name": "Foundation",
"parameters": {
"LEVEL_ELEV": {
"value": -1200
}
}
},
"id": "00413ddd05759bd08a6c8123ca85bd5f"
}
},
# ... snipped for all objects
Which is the response from the server that matches the query made. The payload and transaction is much quicker and smaller than getting all the data.
While the query
parameter on the children
could work to limit the Revit types queried, the Levels are not available as they are a property of the filtered objects. You may notice that the levels might appear multiple times in the data retrieved. This happens because different objects can reference the same level. Duplicate levels can make understanding the structure difficult, so we’ll filter them out in the next step.
A simple Pythonic job to clear that up:
# Initialize an empty set to store unique levels
unique_levels = set()
# Navigate through the nested structure to extract level names and elevations
if (received_data and 'stream' in received_data and
'object' in received_data['stream']):
objects = received_data['stream']['object']\
.get('children', {}).get('objects', [])
for obj in objects:
level_data = obj.get('data', {}).get('level', {})
name = level_data.get('name', 'Unknown')
elevation = level_data.get('parameters', {})\
.get('LEVEL_ELEV', {}).get('value', 'Unknown')
unique_levels.add((name, elevation))
Sorting the levels by elevation can provide a more transparent, more intuitive view of the building structure. This sorted list can be more easily interpreted and valuable for downstream applications or analyses.
sorted_unique_levels = sorted(list(unique_levels), key=lambda x: x[1])
Using this GraphQL query significantly reduces the amount of data you need to fetch from the server. This speeds up the data retrieval process and minimizes the load on both the client and server, making the operation more efficient and cost-effective.