HI,
I have produced a couple of streamlist apps, and I am now trying to write one that can link to a Speckle stream and interrogate a Revit model, ultimately to map materials and constructions to embodied carbon figures.
This is the code I came up with - only a first draft towards a much more articulated final version:
import streamlit as st
from specklepy.api.client import SpeckleClient
# Get the Revit model from Speckle
@st.cache_data()
def get_model(host, token, stream_id):
# initialise the client
client = SpeckleClient(host=host)
# authenticate the client with a token
client.authenticate(token)
# get the stream
# this is a placeholder function. The actual implementation will depend on
# the structure of the Revit model.
model = client.stream.get(stream_id)
# Cache the model data
# st.cache_data('model', model)
return model
# Extract the materials and components from the Revit model
def extract_components(model):
# This is a placeholder function. The actual implementation will depend on
# the structure of the Revit model.
pass
def combine_with_carbon(components, carbon_data):
# This is a placeholder function. The actual implementation will depend on
# the structure of the components and the carbon_data.
pass
def main():
st.title('Embodied Carbon Calculator')
# User input for the embodied carbon figures
carbon_data = {}
materials = ['Material1', 'Material2', 'Material3'] # replace with your actual materials
for material in materials:
carbon_data[material] = st.number_input(f'Enter the embodied carbon figure for {material}:')
# User input for the Speckle server and token
server = st.text_input(
'Enter the Speckle server URL:',
'speckle.xyz',
help="Speckle server to connect."
)
token = st.text_input(
'Enter your Speckle token:',
'',
help="If you don't know how to get your token, take a look at this [link](<https://speckle.guide/dev/tokens.html>)👈"
)
stream_id = st.text_input(
'Enter your Speckle Stream:',
'https://speckle.xyz/streams/eab8e7837c',
)
if server and token:
model = get_model(server, token, stream_id)
components = extract_components(model)
result = combine_with_carbon(components, carbon_data)
st.write(result)
if __name__ == "__main__":
main()
specklepy.logging.exceptions.GraphQLException: GraphQLException: Failed to execute the GraphQL active_user request. Errors: [{‘message’: ‘You do not have the required privileges.’, ‘locations’: [{‘line’: 2, ‘column’: 3}], ‘path’: [‘activeUser’], ‘extensions’: {‘code’: ‘FORBIDDEN’}}]
Ciao @andrea.botti and welcome to these parts!
Judging from the error, there might be something going on with your access token, what scopes did you select when creating it?
I think streams:read is needed for your app.
I am not a Python expert but afais your stream id input is a URL (https://speckle.xyz/streams/eab8e7837c), instead it should be the id of the stream👉(eab8e7837c). We have a StreamWrapper that gives you some handy helpers to deal with URLs. So you can write your code like this:
import streamlit as st
from specklepy.api.client import SpeckleClient
from specklepy.api.wrapper import StreamWrapper
# Get the Revit model from Speckle
@st.cache_data()
def get_model(host, token, stream_id):
# initialise the client
client = SpeckleClient(host=host)
# authenticate the client with a token
client.authenticate(token)
# get the stream
# this is a placeholder function. The actual implementation will depend on
# the structure of the Revit model.
model = client.stream.get(stream_id)
# Cache the model data
# st.cache_data('model', model)
return model
# Extract the materials and components from the Revit model
def extract_components(model):
# This is a placeholder function. The actual implementation will depend on
# the structure of the Revit model.
pass
def combine_with_carbon(components, carbon_data):
# This is a placeholder function. The actual implementation will depend on
# the structure of the components and the carbon_data.
pass
def main():
st.title('Embodied Carbon Calculator')
# User input for the embodied carbon figures
carbon_data = {}
materials = ['Material1', 'Material2', 'Material3'] # replace with your actual materials
for material in materials:
carbon_data[material] = st.number_input(f'Enter the embodied carbon figure for {material}:')
# User input for the Speckle server and token
server = st.text_input(
'Enter the Speckle server URL:',
'speckle.xyz',
help="Speckle server to connect."
)
token = st.text_input(
'Enter your Speckle token:',
'TOKEN_HERE',
help="If you don't know how to get your token, take a look at this [link](<https://speckle.guide/dev/tokens.html>)👈"
)
stream_url = st.text_input(
'Enter your Speckle Stream:',
'https://speckle.xyz/streams/STREAM_ID',
)
# 👇👇👇👇
wrapper = StreamWrapper(stream_url)
stream_id = wrapper.stream_id
# ☝️☝️☝️☝️
if server and token:
model = get_model(server, token, stream_id)
components = extract_components(model)
result = combine_with_carbon(components, carbon_data)
st.write(model)
if __name__ == "__main__":
main()
StreamWrapper also can help with getting authenticated clients and transports. Take a look at this tutorial:
SpeckleException: SpeckleException: Failed to execute the GraphQL commit request. Inner exception: 403 Client Error: Forbidden for url: https://speckle.xyz/graphql
AttributeError: 'SpeckleException' object has no attribute 'referencedObject'
why is it so difficult to authenticate and extract data??
import streamlit as st
from specklepy.api.client import SpeckleClient
from specklepy.api.wrapper import StreamWrapper
def main():
st.title("Stream Access Test")
token = st.text_input(
"Enter your Speckle token:",
"{{TOKEN}}", # don't share your token with anyone!!
)
stream_url = st.text_input(
"Enter your Speckle Stream:",
"https://speckle.xyz/streams/{{STREAM_ID}}",
)
# This now uses the StreamWrapper to shortcut from URL
# to all the details we need
wrapper = StreamWrapper(stream_url)
host = wrapper.host
stream_id = wrapper.stream_id
client = SpeckleClient(host)
client.authenticate_with_token(token)
if client.account.token is not None:
# This checks that we authenticate as the user we expected
st.write(f"Client: {client} ({client.user.account.userInfo.email})")
else:
st.write(
f"Client: {client} ({client.user.account.userInfo.email})",
"Invalid token"
)
return
stream = client.stream.get(stream_id)
st.write("Stream: " + str(stream))
if __name__ == "__main__":
main()
I noticed your example code was defaulting to an ancient token from @gokermu - lets omit that - always good advice to keep those tokens private!!
The tester script revealed for me that under one pyenv, that specklepy was always resolving to the local default account, regardless of authenticating with the token; this only really affects you if you have a number of accounts and servers, but it did cause me some consternation for a short while.
@jonathon thanks mate. This example works, but so did the previous example I had shared until that point (finally!).
It is when I try to take the script ‘further’, i.e. start interrogating data, that receive the error AttributeError: 'SpeckleException' object has no attribute 'referencedObject'
right so which version of which code are we working to get working - as that should be all you need, but I lost track with your posts of both code and tutorials that were different things?
I have a hunch that streamlits data caching may be an arse here, but I’m not a streamlit expert
1. Getting stream and authorising via a token
which was returning the specklepy.logging.exceptions.GraphQLException: error: I have solved that by deleting my personal access token (streams:read apparently was not sufficient) and recreating a new one with everything authorised. I have also tested your minimum example on that and it works well - thanks!
2. Extracting data
Whatever happens later is a bit unclear to me.
I consider myself decent in python and ok with streamlit, but all the object retrieval using the commits and everything happening after that is still a bit confusing for me.
From another tutorial example @gokermu posted ( https://speckle.systems/tutorials/create-your-first-speckle-app-using-only-python/ it seems this can be chosen within the app menus, so it may be a question to mix and match the code from the two.
Specifically here the issue is that the following code section:
# COMMIT 🍀
# gets a commit given a stream and the commit id
commit = client.commit.get(wrapper.stream_id, wrapper.commit_id)
# get obj id from commit
obj_id = commit.referencedObject
# receive objects from commit
commit_data = operations.receive(obj_id, transport)
returns the error: AttributeError: 'SpeckleException' object has no attribute 'referencedObject'
and this happened when I ran the example file (main.py) provided by @gokermu