I have two Revit model files, A and B, where B is linked into A. I want to upload the files to BIM360 Docs via the Autodesk.Forge API and keep them linked, so I can see the combined model in the Forge Model viewer when I subsequently view model A.
I have the two files in a zip file, but from what I understand, I shouldn't upload the zip file, but rather upload A and B separately, then create a relationship between them.
I can upload the files without problems, and I've then tried to link them via this code (using the NON-encoded version ids for A and B):
public async Task SetLinkedFileRelationship(string projectId, string versionId, string linkedVersionId)
{
BaseAttributesExtensionObject baseAttribute = new BaseAttributesExtensionObject("auxiliary:autodesk.core:Attachment", "1.0");
CreateRefDataMeta meta = new CreateRefDataMeta(baseAttribute);
CreateRefData createRefData = new CreateRefData(CreateRefData.TypeEnum.Versions, linkedVersionId, meta);
CreateRef createRef = new CreateRef(new JsonApiVersionJsonapi(JsonApiVersionJsonapi.VersionEnum._0), createRefData);
VersionsApi versionsApi = new VersionsApi { Configuration = { AccessToken = _token.AccessToken } };
await versionsApi.PostVersionRelationshipsRefAsync(projectId, versionId, createRef);
}
...which produces this response:
status: 400
code: FUNCTION_NOT_SUPPORTED
detail: BIM360 currently does not support the creation of refs.
So apparently I can't create the link between A and B like this. Is there another way to accomplish what I want, or is this currently just not possible in BIM360? I know you can do it via the BIM360 Docs web page (using the Upload file -> Linked Files button), but is it possible when I upload the model files via the API? If so, what is the recipe?
Please keep in mind that my question is for uploading to BIM360 Docs - using the Autodesk.Forge API (v2). I'm aware of this post: BIM360 Docs: Setting up external references between files (Upload Linked Files), but that is targeted at manually composing requests. I'd like to be able to use the v2 API.
I believe this post should help https://forge.autodesk.com/blog/bim360-docs-setting-external-references-between-files-upload-linked-files.
Related
I generated (in debug mode an addin) DirectShape as a Volume of a Room as in:
https://github.com/jeremytammik/RoomVolumeDirectShape
Rooms are generated and REVIT file is saved correctly, then I send it to FORGE for processing and I extract Model View Metadata Properties (mvmp) and the OBJ.
The issue is that MVMP does not contain any information of recently generated room volumes, the object is:
DirectShape ds = DirectShape.CreateElement(doc, _id_category_for_direct_shape);
ds.ApplicationId = id_addin;
ds.ApplicationDataId = r.UniqueId;
ds.SetShape(geo.ToList<GeometryObject>());
ds.get_Parameter(_bip_properties).Set(json);
ds.Name = "Room_Max_is_an_okayish_dev_" + r.Name;
The question here is: how should I modify it so it will be included in the extraction mvmp.json?
I suspect I need to tag it somehow or add it to some sort of collection.
Please help.
Thank you in advance.
In Revit, you can define which views are to be included in the Forge translation process.
Ensure that you see your direct shapes in at least one of your Revit views, and that this view is earmarked for translation to Forge.
I am trying to insert an image (PNG) in a Google Slide presentation using the Slides API. I do this by first uploading the image to the user's Drive, obtaining the url, passing that along to the Slide API via the correct request and then deleting the image file.
What used to work as of a few weeks ago:
image_url = '%s&access_token=%s' % (
drive_service.files().get_media(fileId=image_file_id).uri,
creds.token)
However, there have been changes to the Drive API, such that URLS constructed this way no longer work.
I am having difficulty figuring out the new correct URL to use here. The options as per the doc that describes the change are:
Use webContentLink -- Downloads
Use webViewLink -- View
Use exportLinks -- Export
I use code that looks like this to get these links:
upload = drive_service.files().create(
body={'name': 'My Image File'},
media_body=media_body,
fields='webContentLink, id, webViewLink').execute()
image_url = upload.get('webContentLink')
I have tried both #1 and #2 and get the following error:
"Invalid requests[0].createImage: The provided image is in an unsupported format."
I have also been receiving the following error intermittently:
"Invalid requests[0].createImage: Access to the provided image was forbidden."
I verified that I am able to download / view the image from the URLs generated in #1 and #2. I didn't try #3 since I am not trying to export to a different format.
What would be the best way to go about figuring out the correct URL to use?
From your script, I think that the reason of your issue is due to this. By this, the query parameter of access_token cannot be used. Under this situation, when image_url = '%s&access_token=%s' % (drive_service.files().get_media(fileId=image_file_id).uri,creds.token) is used, the login page is returned. By this, such error occurs. So as a workaround, how about the following flow?
Flow:
Upload a PNG file.
Publicly share the PNG file by creating a permission.
Insert the PNG file to Slides.
Close the shared PNG file by deleting the permission.
When the image file is put to the Slides, even when the permission of file is deleted, the image is not removed from the Slides. This workaround uses this.
Sample script:
For above flow, the sample script of python is as follows. Please set the variables of uploadFilename, presentation_id and pageObjectId
uploadFilename = './sample.png' # Please set the filename with the path.
presentation_id = '###' # Please set the Google Slides ID.
pageObjectId = '###' # Please set the page ID of the Slides.
drive = build('drive', 'v3', credentials=creds)
slides = build('slides', 'v1', credentials=creds)
# 1. Upload a PNG file from local PC
file_metadata = {'name': uploadFilename}
media = MediaFileUpload(uploadFilename, mimetype='image/png')
upload = drive.files().create(body=file_metadata, media_body=media, fields='webContentLink, id, webViewLink').execute()
fileId = upload.get('id')
url = upload.get('webContentLink')
# 2. Share publicly the uploaded PNG file by creating permissions.
drive.permissions().create(fileId=fileId, body={'type': 'anyone', 'role': 'reader'}).execute()
# 3. Insert the PNG file to the Slides.
body = {
"requests": [
{
"createImage": {
"url": url,
"elementProperties": {
"pageObjectId": pageObjectId
}
}
}
]
}
slides.presentations().batchUpdate(presentationId=presentation_id, body=body).execute()
# 4. Delete the permissions. By this, the shared PNG file is closed.
drive.permissions().delete(fileId=fileId, permissionId='anyoneWithLink').execute()
Note:
I thought that from your script, you might be using google-api-python-client with python. So I proposed the sample script for python.
In this case, the scopes for using Slides API and Drive API are required. Please be careful this.
In the case of Google Apps Script, you can see the sample script at here.
References:
Upcoming changes to the Google Drive API and Google Picker API
Permissions: create
Permissions: delete
If I misunderstood your question and this was not the direction you want, I apologize.
I was running into the same error even when using the flow involving granting temporary permissions access then removing the permissions after calling .createImage() or .replaceAllShapesWithImage()
I also ran into this error when creating permissions for a folder containing those images: "Invalid requests[0].replaceAllShapesWithImage: Access to the provided image was forbidden." Not sure why the permissions are not propagating to the images...
Following Kos' comment, switching to jpg file type worked for me.
Edit:
It appears I am also required to set the scope to 'https://www.googleapis.com/auth/drive' in order for it to work, which isn't ideal, but is sufficient for now.
Edit 2:
Nevermind it appears to be inconsistent. I am running into the permissions access error again. Deleting my token.pickle does not seem to fix either
I have a two-part WPF App and Revit Addin that runs a series of routines to create and activate a new BIM 360 project via the FORGE BIM 360 API, launches Revit and creates and sets up a collection of Revit models from a "seed" Revit model. The primary requirement is to have zero user interaction in this process: to be fully automated.
Given that background, I am having trouble linking the newly saved BIM 360 models to one-another. I have the option to do this either in the Revit Addin with Revit API hooks (preferred) or through the manager WPF App with FORGE API REST calls after the models are created.
Is either one possible?
I have successfully linked with the cached local models as described here and here. However, this does not meet the requirement fully, since when any other user (besides the automation machine user) opens the model the links are Not Found.
Screenshot of links Not Found.
Current "sort-of-working" code:
var wId = GetWorksetId(doc, w);
if (wId != null)
{
string localPath = settings.CloudModels.Where(x => x.ModelName == _linkModelNames[i]).Select(x => x.LocalFilePath).First();
ModelPath path = ModelPathUtils.ConvertUserVisiblePathToModelPath(localPath);
using (var options = new RevitLinkOptions(true))
{
using (var t = new Transaction(doc, w))
{
t.Start();
doc.GetWorksetTable().SetActiveWorksetId(wId);
using (var result = RevitLinkType.Create(doc, path, options))
{
_ = RevitLinkInstance.Create(doc, result.ElementId);
}
t.Commit();
linkPlaced++;
}
}
}
I was able to get the correct ModelPath via the Revit API thanks to this helpful tip. I can save this value in my Addin, close the model, and access the property later after opening a different model (saved in the CloudModels class referenced in the linq statement in the code snippet above). Unfortunately for me RevitLinkType.Create() that takes a ModelPath does not accept the cloud path, so I may have hit another dead end. Unless it is possible with an ExternalResourceReference. Has anyone tried this option? If so, how do you assemble a Revit ExternalResourceReference? I am not familiar with this process, and looking over this course from AU 2017, I don't see that it necessarily applies to BIM 360 cloud models. A BIM 360 cloud example would be very helpful if this is possible.
Alternate strategy: I do not see any reference to loading links in the FORGE Data Management API or other FORGE APIs. If I have somehow missed it, please share a link.
Any help would be very much appreciated!!
EDIT: I have since found these two (1) (2) similar questions that, at least for my purposes, were not answered satisfactorily. Any updates I should be aware of?
As of now (Jan 2020), unfortunately, we do not have a Link API for cloud models. It is on the roadmap.
Revit API 2022 docs mention that
The methods:
RevitLinkType.Create(Document, ModelPath, RevitLinkOptions)
RevitLinkType.LoadFrom(ModelPath, WorksetConfiguration)
have been enhanced to support creation of new cloud model Revit links.
You may use ModelPathUtils.ConvertCloudGUIDsToCloudPath() to
create a cloud path to use as an argument to these methods.
Additional Resource:
This Youtube Video showcases an example and its respective Github repo.
For Revit 2021 and below, you can use ExternalResourceReference() as a workaround, but I've noticed that this is not always reliable.
Its also mentioned in the documentation of InSessionPath property to not rely on this property:
Do not rely on this path (InSessionPath ) to look up an ExternalResourceReference, as the path is neither unique nor stable. It isn't unique because multiple servers might use the same server name and display name format. It isn't stable because some servers allow renaming, and because a server might change its name at some point.
Below is the code to do that:
var linkCloudPath = doc.GetCloudModelPath(); // the cloudpath of a BIM360 model
Guid linkedmodelguid = linkCloudPath.GetModelGUID();
Guid linkedprojectguid = linkCloudPath.GetProjectGUID();
Dictionary<string, string> Dictionary_ExternalResource = new Dictionary<string, string>(){
{"LinkedModelModelId", modelGuid.ToString()},
{"LinkedModelProjectId", projGuid.ToString()}
};
Dictionary<string, Guid> servers = new Dictionary<string, Guid>();
foreach (var service in ExternalServiceRegistry.GetServices())
{
if (service.Name == "External Resource Service")
{
IList<Guid> server_ids = service.GetRegisteredServerIds();
foreach (var server_id in server_ids)
{
servers.Add(service.GetServer(server_id).GetName(), server_id);
}
}
}
Guid BIM360ServerID = servers["BIM 360"];
ExternalResourceReference ERS = new ExternalResourceReference(BIM360ServerID, Dictionary_ExternalResource, "", "");
RevitLinkOptions options = new RevitLinkOptions(false);
LinkLoadResult result = RevitLinkType.Create(gcdoc, ERS, options);
RevitLinkInstance.Create(gcdoc, result.ElementId);
Please note that this seems to be working fine in Revit 2020, but not in Revit 2021.
I believe it is possible to create links to the Cloud Models in Revit 2019 or higher (with ModelPathUtils.ConvertCloudGUIDsToCloudPath()). You'll need the ProjectGUID and ModelGUID to make the cloud model path.
Regarding the ExternalResource approach, that also works - but it's super messy - you can read the properties associated with existing BIM360 links and you'll see how an ExternalResource is defined for BIM360 links.
Finally - as of today, the Forge Design Automation for Revit approach would not work for you at all:
1. Not possible to open a live cloud workshared model (only published/uploaded models).
2. No network access while you're running in a Design Automation for Revit session.
Good luck...
-Matt
I’m using Xamarin Forms to do some cross platform applications and I’d like to offer DropBox and GoogleDrive as places where users can do backups, cross platform data sharing and the like. I was able to get DropBox working without doing platform specific shenanagins just fine, but Google Drive is really giving me fits. I have my app setup properly with Google and have tested it with a regular CLI .NET application using their examples that read the JSON file off the drive and create a temporary credentials file – all fine and well but getting that to fly without access to the file system is proving elusive and I can’t find any examples on how to go about it.
I’m currently just using Auth0 as a gateway to allow users to provide creds/access to my app for their account which works dandy, the proper scope items are requested (I’m just using read only file access for testing) – I get an bearer token and refresh token from them – however when trying to actually use that data and just do a simple file listing, I get a 400 bad request error.
I’m sure this must be possible but I can’t find any examples anywhere that deviate from the slightest of using the JSON file downloaded from Google and creating a credentials file – surely you can create an instance of the DriveService object armed with only the bearer token...
Anyway – here’s a chunk of test code I’m trying to get the driveService object configured – if anyone has done this or has suggestions as to what to try here I’d very much appreciate your thoughts.
public bool AuthenticationTest(string pBearerToken)
{
try
{
var oInit = new BaseClientService.Initializer
{
ApplicationName = "MyApp",
ApiKey = pBearerToken,
};
_googleDrive = new DriveService(oInit);
FilesResource.ListRequest listRequest = _googleDrive.Files.List();
listRequest.PageSize = 10;
listRequest.Fields = "nextPageToken, files(id, name)";
//All is well till this call to list the files…
IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute().Files;
foreach (var file in files)
{
Debug. WriteLine(file.Name);
}
}
catch (Exception ex)
{
RaiseError(ex);
}
}
I am using the Box windows V2 SDK to upload files to my Box account using the following code:
BoxFileRequest request = new BoxFileRequest()
{
Parent = new BoxRequestEntity() { Id = "0" },
Name = attachment.Name,
Description = "This is failing to be sent..."
};
client.FilesManager.UploadAsync(request, new MemoryStream(attachment.FileContent)).Result;
Uploading the file works great. However, I can not get the description field sent to the box server. Is it possible to upload a file with a description, or do I have to call FilesManager.UpdateInformationAsync after the file has been uploaded to accomplish this? It would be nice if this was an option so I could reduce the number of API calls..
The description must be set in a separate API request after uploading the file.
We have heard reusing some of the request objects may cause some confusion on what can be done with each request. We are evaluating whether or not this should be changed