Petite introduction a l'API Autodesk Plateforme Service directement (APS) depuis Dynamo (sur la plateforme ACC)
#DynamoBIM #Revit #Python #ACC #APS
#AutodeskExpertElite #AutodeskCommunity 
if this article is not in your language, use the Google Translate
widget ⬈ (bottom of page for Mobile version ⬇)
Dans cet article, à l'aide d'APS, nous verrons :
- comment enregistrer une WEB Application
- comment s'authentifier depuis une application tierce (ici Dynamo)
- un 1ᵉʳ exemple pour lister les utilisateurs des Projets ACC
Dans la suite de cet article, nous utiliserons Dynamo3 + IronPython3
et la librairie .Net Newtonsoft Json.
Vous trouverez dans un prochain article un exemple avec CPython3 et la libraire python json
Vous trouverez dans un prochain article un exemple avec CPython3 et la libraire python json
- Introduction
APS, ou Autodesk Platform Services, anciennement connu sous le
nom de Forge, est une suite de services cloud offerte par
Autodesk. Elle permet aux développeurs de créer des
applications personnalisées, d'intégrer des flux de travail et
d'automatiser des processus, grâce à plusieurs APIs bien
documentées.
Nous utiliserons dans cet article les API suivantes
- Authentification API
- Autodesk Construction Cloud Platform (ACC) API
- Configuration initiale
Avant de pouvoir utiliser les APIs, APS, il est nécessaire
d'enregistrer l'application pour obtenir un identifiant et un secret
client. Pour plus d'informations, consultez ce
tutoriel : Création d'une application APS
- Authentification et Scopes
La connexion à APS, depuis Dynamo, se fait (dans cet exemple) via
une authentification en deux étapes (2-Legged Token). Ce processus
assure une sécurité renforcée pour l'accès aux APIs.
Nous devons également définir les 'scopes' suivant :
data:read
/
account:read
import sys
import clr
import System
from System.Collections.Generic import List, IList, Dictionary
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:write data:search account:read" ,
}
#
# Content-Type is set to "application/x-www-form-urlencoded". For x-www-form-urlencoded, we need to URL-encode the data
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
token = get_auth_token("CLIENT_ID_KEY", "CLIENT_SECRET_KEY")
OUT = token
- Exemple : lister les utilisateurs par projet
Pour cela, nous utilisons la fonction suivante
https://aps.autodesk.com/en/docs/acc/v1/reference/http/admin-projectsprojectId-users-GET/
voici le code
# Load the Python Standard and DesignScript Libraries
import sys
import clr
import System
from System.Collections.Generic import List, IList, Dictionary
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
my_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments)
import logging
import traceback
import time
import re
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:write data:search account:read" ,
}
#
# Content-Type is set to "application/x-www-form-urlencoded". For x-www-form-urlencoded, we need to URL-encode the data
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
def post_request_data_management(url, dict_content={}, dict_headers={}):
global token
response_data = None
# Serialize to JSON
jsonRequest = json.dumps(dict_content).encode('utf-8')
# Set headers
headers = dict_headers
# Prepare the POST request
if dict_content:
request = urllib.request.Request( url, data=jsonRequest, headers=headers, method='POST' )
else:
request = urllib.request.Request( url, headers=headers, method='POST' )
try:
# Send request and parse response
with urllib.request.urlopen(request) as response:
result = response.read().decode('utf-8')
print("jsonResponse:", result)
response_data = json.loads(result)
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 response_data
out = []
dict_project_users = {}
my_hub_name = "MY_HUB"
# get token
token_type = None
token = get_auth_token("CLIENT_ID_KEY", "CLIENT_SECRET_KEY")
# get hub id
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)
accountId = my_hub_id #re.sub(r"^b.", "", my_hub_id)
# 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"]
for data in data_array_projects:
projectId = data["id"]
projectName = data["attributes"]["name"]
#
dict_project_users[projectName] = []
#
j_project_users = get_request_data_management(f"https://developer.api.autodesk.com/construction/admin/v1/projects/{projectId}/users")
if j_project_users is not None:
data_array_users = j_project_users["results"]
for user in data_array_users:
email = user['email']
name = user['name']
role = ""
for i in user["roles"]:
role += i['name'] + ", "
dict_project_users[projectName].append([email, name, role])
OUT = dict_project_users
.
- Rajout de Paramètres a la requête (Query String Parameters)
Vous pouvez appliquer des filtres de requête grâce aux paramètres
disponible avec cette fonction.
Voici un exemple ou filtre par email et l'on modifie le
nombre maximum d'utilisateurs retournés.
Ici les filtres se mettent en fin d'URI suivant la syntaxe suivante :
Ici les filtres se mettent en fin d'URI suivant la syntaxe suivante :
URI
?
filtre1&
filtre2
j_project_users = get_request_data_management("https://developer.api.autodesk.com/construction/admin/v1/projects/{projectId}/users?filter[email]=snee&filterTextMatch=contains&limit=50")
un autre exemple pour obtenir les métas-datas incluant le chemin
j_data_file = get_request_data_management(f"https://developer.api.autodesk.com/data/v1/projects/{my_project_id}/items/{file_id}?includePathInProject=true")
pathInProject = j_data_file["data"]["attributes"]["pathInProject"]
folder_name = pathInProject.split("/")[-1]
Voici une bonne documentation sur les conventions d'URI
https://www.odata.org/documentation/odata-version-2-0/uri-conventions/
https://www.odata.org/documentation/odata-version-2-0/uri-conventions/
- Plus rapide ?
Vous pouvez également tenter d'utiliser du multi-threading, mais attention
à la limite des quotas imposés par l'API APS
https://aps.autodesk.com/en/docs/data/v2/developers_guide/rate-limiting/forge-rate-limits/
https://aps.autodesk.com/en/docs/data/v2/developers_guide/rate-limiting/dm-rate-limits/
https://aps.autodesk.com/en/docs/data/v2/developers_guide/rate-limiting/dm-rate-limits/
le même code avec des Threads
# Load the Python Standard and DesignScript Libraries
import sys
import clr
import System
from System.Collections.Generic import List, IList, Dictionary
from System.Threading import Thread, ThreadStart
import traceback
import time
import re
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:write data:search account:read" ,
}
#
# Content-Type is set to "application/x-www-form-urlencoded". For x-www-form-urlencoded, you need to URL-encode the data
# URL encode the payload!
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
def get_users_project(data):
dict_users = {}
projectId = data["id"]
projectName = data["attributes"]["name"]
#
dict_users[projectName] = []
#
j_project_users = get_request_data_management(f"https://developer.api.autodesk.com/construction/admin/v1/projects/{projectId}/users")
if j_project_users is not None:
data_array_users = j_project_users["results"]
for user in data_array_users:
email = user['email']
name = user['name']
role = ""
for i in user["roles"]:
role += i['name'] + ", "
dict_users[projectName].append([email, name, role])
return dict_users
class Worker():
__slots__ = 'fn', 'args', 'result'
def __init__(self, fn, args):
self.fn = fn
self.args = args
self.result = None
def __call__(self):
self.result = self.fn(*self.args)
start = time.time()
out = []
my_hub_name = "MY_HUB"
# get token
token_type = None
token = get_auth_token("CLIENT_ID_KEY", "CLIENT_SECRET_KEY")
# get hub id
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)
accountId = my_hub_id #re.sub(r"^b.", "", my_hub_id)
# 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"]
# set workers
workers, tasks = [], []
for data in data_array_projects:
w = Worker(get_users_project, (data,))
t = Thread(ThreadStart(w))
workers.append(w)
tasks.append(t)
t.Start()
for t in tasks: t.Join()
# merge all dict
out_dict = {}
for x in workers:
out_dict = dict(out_dict, **x.result)
elapsed = time.time() - start
OUT = elapsed, out_dict
Dans un prochain article, nous verrons un exemple un peu plus complexe
(recherche de fichiers au sein d'un projet).
« Il vaut mieux être un optimiste qui se trompe parfois qu'un pessimiste
qui a toujours raison. »
Mark Twain
0 commentaires:
Enregistrer un commentaire