23 juin 2024

[Dynamo += Python] Introduction Autodesk Plateforme Services (Partie 1/2)

  




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  :
  1. comment enregistrer une WEB Application 
  2. comment s'authentifier depuis une application tierce (ici Dynamo) 
  3. 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

  • 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 System.Text;
from System.Text import Encoding

clr.AddReference('Newtonsoft.Json')
import Newtonsoft.Json;
import Newtonsoft.Json.Linq;
from Newtonsoft.Json.Linq import JObject, JArray, JToken

clr.AddReference('System.Net.Http')
import System.Net.Http
from System.Net.Http import *

def get_auth_token(client_id, secret_id):
    b_64_pass = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(client_id + ":" + secret_id))
    token = None
    with HttpClient() as client:
        client.DefaultRequestHeaders.Add("Authorization", "Basic " + b_64_pass)
        parameters = Dictionary[System.String, System.String]()
        parameters.Add("Content-Type", "application/x-www-form-urlencoded")
        parameters.Add("Accept", "application/x-www-form-urlencoded")
        parameters.Add("grant_type", "client_credentials")
        parameters.Add("scope", r"data:read account:read")
        response = client.PostAsync("https://developer.api.autodesk.com/authentication/v2/token", FormUrlEncodedContent(parameters)).Result
        result = response.Content.ReadAsStringAsync().Result
        jobject = JObject.Parse(result)
        token_type = jobject["token_type"]
        token = jobject["access_token"].ToString()
    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


import sys
import clr
import System
from System.Collections.Generic import List, IList, Dictionary

import System.Text;
from System.Text import Encoding
clr.AddReference('Newtonsoft.Json')
import Newtonsoft.Json
import Newtonsoft.Json.Linq
from Newtonsoft.Json.Linq import JObject, JArray, JToken

clr.AddReference('System.Net.Http')
import System.Net.Http
from System.Net.Http import *


def get_auth_token(client_id, secret_id):
    b_64_pass = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(client_id + ":" + secret_id))
    token = None
    with HttpClient() as client:
        client.DefaultRequestHeaders.Add("Authorization", "Basic " + b_64_pass)
        parameters = Dictionary[System.String, System.String]()
        parameters.Add("Content-Type", "application/x-www-form-urlencoded")
        parameters.Add("Accept", "application/x-www-form-urlencoded")
        parameters.Add("grant_type", "client_credentials")
        parameters.Add("scope", r"data:read account:read")
        response = client.PostAsync("https://developer.api.autodesk.com/authentication/v2/token", FormUrlEncodedContent(parameters)).Result
        result = response.Content.ReadAsStringAsync().Result
        jobject = JObject.Parse(result)
        token = jobject["access_token"].ToString()
    return token

def get_request_data_management(url, main_item_id="", type_items_request =""):
    global token
    jobjectA = None
    with HttpClient() as client:
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token)
        response = client.GetAsync(url + main_item_id + type_items_request, HttpCompletionOption.ResponseContentRead).Result
        resultA = response.Content.ReadAsStringAsync().Result
        jobjectA = JObject.Parse(resultA)
    return jobjectA
    
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 = JArray(jobject_hubs_data["data"])
my_hub_id = next((data["id"].ToString() for data in data_hubs_array if data["attributes"]["name"].ToString() == my_hub_name), None)
# get project id
jobject_projects_data = get_request_data_management("https://developer.api.autodesk.com/project/v1/hubs/", my_hub_id, type_items_request ="/projects")
data_array_projects = JArray(jobject_projects_data["data"])
for data in data_array_projects:
    projectId = data["id"].ToString()
    projectName = data["attributes"]["name"].ToString()
    #
    dict_project_users[projectName] = []
    #
    j_project_users = get_request_data_management("https://developer.api.autodesk.com/construction/admin/v1/projects/" , projectId, type_items_request ="/users")
    data_array_users = JArray(j_project_users["results"])
    for user in data_array_users:
        email = user['email'].ToString()
        name = user['name'].ToString()
        role = ""
        for i in user["roles"]:
            role += i['name'].ToString() + ", "
        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 :
?filtre1&filtre2


j_project_users = get_request_data_management("https://developer.api.autodesk.com/construction/admin/v1/projects/" , 
                                                projectId, type_items_request ="/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("https://developer.api.autodesk.com/data/v1/projects/" , my_project_id, type_items_request ="/items/{}?includePathInProject=true".format(file_id))
pathInProject = j_data_file["data"]["attributes"]["pathInProject"].ToString()
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/

  • Plus rapide ?
Vous pouvez également tenter d'utiliser du multi-threading, mais attention à la limite des quotas imposés par l'API APS


le même code avec des Threads 

import sys
import clr
import System
from System.Collections.Generic import List, IList, Dictionary

from System.Threading import Thread, ThreadStart

import System.Text
from System.Text import Encoding
clr.AddReference('Newtonsoft.Json')
import Newtonsoft.Json
import Newtonsoft.Json.Linq
from Newtonsoft.Json.Linq import JObject, JArray, JToken

clr.AddReference('System.Net.Http')
import System.Net.Http
from System.Net.Http import *

import time

def get_auth_token(client_id, secret_id):
    b_64_pass = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(client_id + ":" + secret_id))
    token = None
    with HttpClient() as client:
        client.DefaultRequestHeaders.Add("Authorization", "Basic " + b_64_pass)
        parameters = Dictionary[System.String, System.String]()
        parameters.Add("Content-Type", "application/x-www-form-urlencoded")
        parameters.Add("Accept", "application/x-www-form-urlencoded")
        parameters.Add("grant_type", "client_credentials")
        parameters.Add("scope", r"data:read data:write data:search account:read")
        response = client.PostAsync("https://developer.api.autodesk.com/authentication/v2/token", FormUrlEncodedContent(parameters)).Result
        result = response.Content.ReadAsStringAsync().Result
        jobject = JObject.Parse(result)
        token_type = jobject["token_type"]
        token = jobject["access_token"].ToString()
    return token

def get_request_data_management(url, main_item_id="", type_items_request =""):
    global token
    jobjectA = None
    with HttpClient() as client:
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token)
        response = client.GetAsync(url + main_item_id + type_items_request, HttpCompletionOption.ResponseContentRead).Result
        resultA = response.Content.ReadAsStringAsync().Result
        jobjectA = JObject.Parse(resultA)
    return jobjectA


def get_users_project(data):
    dict_users = {}
    projectId = data["id"].ToString()
    projectName = data["attributes"]["name"].ToString()
    #
    dict_users[projectName] = []
    #
    j_project_users = get_request_data_management("https://developer.api.autodesk.com/construction/admin/v1/projects/" , projectId, type_items_request ="/users")
    data_array_users = JArray(j_project_users["results"])
    for user in data_array_users:
        email = user['email'].ToString()
        name = user['name'].ToString()
        role = ""
        for i in user["roles"]:
            role += i['name'].ToString() + ", "
        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 = []
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 = JArray(jobject_hubs_data["data"])
my_hub_id = next((data["id"].ToString() for data in data_hubs_array if data["attributes"]["name"].ToString() == my_hub_name), None)
# get project id
jobject_projects_data = get_request_data_management("https://developer.api.autodesk.com/project/v1/hubs/", my_hub_id, type_items_request ="/projects")
data_array_projects = JArray(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