Crumple: Collect.RevitLinks

Hi @GavinNicholls

With your node Collect.RevitLinks, how can I get an extra output for the link document ??
(currently outputs linkType and linkInstances)

i.e similar to an old Archi-lab node ‘Get Documents’

thanks

Andrew

Use the Revit.GetDocument node. That will get the document for a link instance.

I also have a similar node called Revit.GetDocuments which also rounds up documents that are not active in addition.

Hi @GavinNicholls
thanks for the fast reply

What if I need both the LinkInstance plus the doc ??
Reason being- I want to get the ACC attributes and push them into the linkInstance parameters

I wounder if this might be easier to find via the TransmissionData class… Not sure how that manages cloud paths, though I assume it would have to have both the project and model GUIDs.

What is the reasoning for pushing those GUIDs into the link?

ACC documents will be a bit different I think, but the link would still be parallel to the document it represents as an output. Noting I do deal with the raw DB Document output type.

You can’t push data into a linked document instance to my understanding, it’s not like a live document. You have to programmatically open the document itself and set the data in there unless I’m misunderstanding your task. I’d look into Dynamo multiplayer for that.

If you mean as a shared parameter on the link instance object, that should work as is I guess. Pull the data out of the link instance’s doc, then set it to its project parameters at the host document level.

thanks @jacob.small & @GavinNicholls

It might help to explain what i’m trying to achieve.

For big projects with many models, there is (model level) data in ACC which is useful, such as:

model description
last modified date
ACC path
etc

You can’t see any of that data in Revit, unless you backtrack from Revit (using the link name, or the path from ‘manage links’)

So the logic is:

  • query all the linked models
  • retrieve relevant details from ACC for each link
  • push those details into link instance parameters. This could be built in parameters such as comments, name or mark, or custom parameters. Name & mark seem to be given auto-incremented digits- so the tenth linked model is given a name & mark of ‘10’ which is not exactly useful

Andrew

I think once you hit the ‘from acc’ part, dynamo is limited. My understanding is APS/forge is needed for that type of query but happy to be proven wrong.

1 Like

So your missing a few steps in the workflow. For now let’s ignore the Dynamo bit and focus on the steps you will have to take by hand to get this info. One thing to note, there is no such thing as a ‘path’ in APS. All things including cloud models, files, and folders are just objects in the project.

  1. Starting in Revit, select a link instance.
  2. Get the link type as this is where the path is stored.
  3. Get the model path from the link type. For cloud links this will be the product GUID and model GUID. For other link types this will be a network path. If it is not a cloud link stop here you have what you need.
  4. Leave Revit and move over to APS, as the other data you want is not stored in the model.
  5. Find the ACC project by it’s GUID. Handle access issues here as well since the end user might not have access to the particular path.
  6. Within the project find the object by GUID. Again, you may have to deal with access issues here.
  7. From the object, extract the title, description and whatever else’s you like.
  8. From the object, get the list of parent folders.
  9. From the list of parent folders, extract the titles.
  10. Assemble the titles of the parent folders into your directory structure, appending the object title if desired.
  11. Write the data you collected from APS (steps 6 and 9) to the respective parameters in the Revit element (I would use the link type not the instances).

Alternatively for steps 7-9 can just extract the web link to the model’s parent folder and have the user click that to go there (assuming they have access rights).

Sadly steps 1-10 (or 1-8) involve two application APIs - Revit and Autodesk Construction Cloud via Aurodesk Platform Services. The former you have already accomplished to some degree and I know you’re capable and familiar with in the context of Dynamo. However it could also be done via Python, Macro, Add-In, or even Design Automation using APS. The later requires using some web development skills, which also entails authenticating your user or your application. To complicate things further any time someone updates a property, moved a file on ACC, or changes a link path in Revit (so there are triggers in the desktop and the could environment) which make running this often or on trigger a must. That’s a problem for another day.

To build a Dynamo version of the APS tool is no small feat, though it also isn’t any harder than building it as a web app, macro, desktop tool, or Revit add-in. First learn how to manage what you are after on the web though. Tools like Postman can simplify the learning aspect as it allows calling APIs without requiring that you build the UI or work with a debugger to read results.

Perhaps more fortunately the APS aspect is simple and common enough that there are good tutorials for most of it.

Start here: https://get-started.aps.autodesk.com/

Then move onto here: Data Management API | Autodesk Platform Services (APS)

And finally build the toolset again in the automation method of your choice.

3 Likes

hi,

it’s necessary to get urn too, here is an example of how to obtain some datas from an link using APS.

Python Code (CPython3 or PythonNet3)
import clr
import sys
import System

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

import traceback
import json
import urllib.request
import urllib.error
import json
import ssl
from base64 import b64encode

def get_auth_token(client_id, secret_id):
    url = "https://developer.api.autodesk.com/authentication/v2/token"
    client_credentials = f"{client_id}:{secret_id}"
    b_64_pass = b64encode(client_credentials.encode("utf-8")).decode("utf-8")
    #
    token = None
    payload = {
    "grant_type": "client_credentials", 
    "scope": "data:read data:search" ,
    }
    #
    json_data = urllib.parse.urlencode(payload).encode('utf-8')
    # Create request with headers
    request = urllib.request.Request(
        url,
        data=json_data,
        headers={
            "Authorization": "Basic {}".format(b_64_pass),
            "Content-Type": "application/x-www-form-urlencoded",
            "Accept": "application/json",
        },
        method='POST'
    )
    
    try:
        # Send request
        with urllib.request.urlopen(request) as response:
            status_code = response.getcode()
            response_data = json.loads(response.read().decode('utf-8'))
            token_type = response_data["token_type"]
            token = response_data["access_token"]
    #
    except urllib.error.HTTPError as e:
        print(f"Status Code: {e.code}")
        print(traceback.format_exc())
        try:
            error_data = json.loads(e.read().decode('utf-8'))
            print(f"Response: {error_data}")
        except:
            print(f"Response: Error {e.code} - {e.reason}")
    #
    except Exception as e:
        print(traceback.format_exc())
    return token
    
    
def get_request_data_management(url):
    global token
    response_data = None
    # Prepare headers
    headers = {"Authorization": f"Bearer {token}", "Accept": "application/json" }

    # Build the request
    request = urllib.request.Request(url, headers=headers, method='GET' )
    try:
        # Send request
        with urllib.request.urlopen(request) as response:
            resultA = response.read().decode('utf-8')
            response_data = json.loads(resultA)
    except urllib.error.HTTPError as e:
        print(f"Status Code: {e.code}")
        try:
            error_data = json.loads(e.read().decode('utf-8'))
            print(f"Response: {error_data}")
        except:
            print(f"Response: Error {e.code} - {e.reason}")
    except Exception as e:
        print(traceback.format_exc())

    return response_data
    

#Preparing input from dynamo to revit
linkInstance = UnwrapElement(IN[0])
linkdoc = linkInstance.GetLinkDocument()
path = linkdoc.GetCloudModelPath()
model_urn = linkdoc.GetCloudModelUrn()
my_project_Guid = path.GetProjectGUID()
model_Guid = path.GetModelGUID()

data_link = {}

my_hub_name = "MY_HUB_NAME"
name_project_acc = "MY_PROJECT_NAME"
# get token
token_type = None
token = get_auth_token("CLIENT_ID_KEY", "CLIENT_SECRET_KEY")

jobject_hubs_data = get_request_data_management("https://developer.api.autodesk.com/project/v1/hubs/")
#
data_hubs_array = jobject_hubs_data["data"]
my_hub_id = next((data["id"] for data in data_hubs_array if data["attributes"]["name"] == my_hub_name), None)
#
# get project id
jobject_projects_data = get_request_data_management(f"https://developer.api.autodesk.com/project/v1/hubs/{my_hub_id}/projects")
data_array_projects = jobject_projects_data["data"]
my_project_id = next((data["id"] for data in data_array_projects if data["attributes"]["name"] == name_project_acc), None)
#
# get file info fom urn
jobject_versions = get_request_data_management(f"https://developer.api.autodesk.com/data/v1/projects/{my_project_id}/items/{model_urn}/versions")
data_versions_array = jobject_versions["data"]
for j_version in data_versions_array:
    data_extension = j_version["attributes"]["extension"]["data"]
    if data_extension["modelGuid"] == model_Guid.ToString():
        data_link = j_version["attributes"]
        break

OUT = data_link

I couldn’t get the ACC Id of the ACC project directly from the Revit API (only the urn of the model), if anyone has a more direct solution I’m interested.

edit, fixed, see the next post Crumple: Collect.RevitLinks - #11 by c.poupin

Resources:

Note

if you have the id (ACC) of the host model cloud, you can use this method to get RevitLinks

2 Likes

The Document has the GetProjectId() method. This appears to be from the Data Management API.
It is also possible to get additional ACC link information from the RevitLinkType elements that doesn’t require the loading in of the documents - much faster

import clr

clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

doc = DocumentManager.Instance.CurrentDBDocument

def get_ref_info(revit_link_type):
    ref = next(iter(revit_link_type.GetExternalResourceReferences().Values))
    return ref.GetReferenceInformation()

info_from_type = [
    get_ref_info(rlt) for rlt in FilteredElementCollector(doc).OfClass(RevitLinkType)
]

OUT = info_from_type

Expurgated example

3 Likes

thanks, I’d overlooked it, so here’s the simplified example

import clr
import sys
import System

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

import traceback
import json
import urllib.request
import urllib.error
import json
import ssl
from base64 import b64encode

def get_auth_token(client_id, secret_id):
    url = "https://developer.api.autodesk.com/authentication/v2/token"
    client_credentials = f"{client_id}:{secret_id}"
    b_64_pass = b64encode(client_credentials.encode("utf-8")).decode("utf-8")
    #
    token = None
    payload = {
    "grant_type": "client_credentials", 
    "scope": "data:read data:search" ,
    }
    #
    json_data = urllib.parse.urlencode(payload).encode('utf-8')
    # Create request with headers
    request = urllib.request.Request(
        url,
        data=json_data,
        headers={
            "Authorization": "Basic {}".format(b_64_pass),
            "Content-Type": "application/x-www-form-urlencoded",
            "Accept": "application/json",
        },
        method='POST'
    )
    
    try:
        # Send request
        with urllib.request.urlopen(request) as response:
            status_code = response.getcode()
            response_data = json.loads(response.read().decode('utf-8'))
            token_type = response_data["token_type"]
            token = response_data["access_token"]
    #
    except urllib.error.HTTPError as e:
        print(f"Status Code: {e.code}")
        print(traceback.format_exc())
        try:
            error_data = json.loads(e.read().decode('utf-8'))
            print(f"Response: {error_data}")
        except:
            print(f"Response: Error {e.code} - {e.reason}")
    #
    except Exception as e:
        print(traceback.format_exc())
    return token
    
    
def get_request_data_management(url):
    global token
    response_data = None
    # Prepare headers
    headers = {"Authorization": f"Bearer {token}", "Accept": "application/json" }

    # Build the request
    request = urllib.request.Request(url, headers=headers, method='GET' )
    try:
        # Send request
        with urllib.request.urlopen(request) as response:
            resultA = response.read().decode('utf-8')
            response_data = json.loads(resultA)
    except urllib.error.HTTPError as e:
        print(f"Status Code: {e.code}")
        try:
            error_data = json.loads(e.read().decode('utf-8'))
            print(f"Response: {error_data}")
        except:
            print(f"Response: Error {e.code} - {e.reason}")
    except Exception as e:
        print(traceback.format_exc())

    return response_data
    
#Preparing input from dynamo to revit
linkInstance = UnwrapElement(IN[0])
linkdoc = linkInstance.GetLinkDocument()
path = linkdoc.GetCloudModelPath()
model_urn = linkdoc.GetCloudModelUrn()
project_id = linkdoc.GetProjectId()
my_project_Guid = path.GetProjectGUID()
model_Guid = path.GetModelGUID()

data_link = {}

# get token
token_type = None
token = get_auth_token("CLIENT_ID_KEY", "CLIENT_SECRET_KEY")
#
# get file fom urn
jobject_versions = get_request_data_management(f"https://developer.api.autodesk.com/data/v1/projects/{project_id}/items/{model_urn}/versions")
data_versions_array = jobject_versions["data"]
for j_version in data_versions_array:
    data_extension = j_version["attributes"]["extension"]["data"]
    if data_extension["modelGuid"] == model_Guid.ToString():
        data_link = j_version["attributes"]
        break

OUT = data_link
3 Likes

A bit out of the scope of the thread - if you have access to the Data Model API, GraphQL absolutely rips! Currently using with Postman - only need the project urn

query ElementGroupsByProject($projectId: ID!) {
    elementGroupsByProject(projectId: $projectId) {
        results {
            id
            name
            lastModifiedOn
            version {
                versionNumber
                createdOn
            }
        }
    }
}

Result with id’s removed

3 Likes

thanks @Mike.Buttery @c.poupin

Some great info there- I’ll play around with it

Andrew

1 Like