Ways to determine whether the current RVT file has been saved

Is there any node or Revit API that can be introduced into Python to determine whether the current RVT file has been saved (if the file has been modified, whether it has been saved to the latest version, etc.)?

I seem to have found a similar API, but I don’t know how to use it in Python Script node.
API

Hi @b10705001F2HWP probably this one here from genius loci could work

3 Likes

If that isn’t what you are after the BasicFileInfo class and the DocumentVersion class can likely help (though it isn’t exactly clear to me what you are after).

4 Likes

I have tried using Python to import IsModified Property of Document Class.
But after modifying the RVT model, regardless of whether I save it or not, the return value of the Python node is False.

In addition, Python also reported an error when introducing the HasAllChangesFromCentral and GetChangedElements methods.
I also tried to use Dyn nodes, but the results were also strange (SaveInCurrent always returns true, SaveInLater always returns false).


image


IsModified Property

Hi,

here an example using GetChangedElements method

is_saved_or_not


import clr
import sys
import System
from System.Collections.Generic import List, IList, Dictionary
from System.IO import StringReader, Path, File, SearchOption 
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#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
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

local_version = Document.GetDocumentVersion(doc)
#
if doc.IsModelInCloud :
	cloud_ModelPath = doc.GetCloudModelPath()
	model_guid = cloud_ModelPath.GetModelGUID().ToString()
	AppDataList = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData).split("\\")
	AppDataList.pop(-1)
	AppData = "\\".join(AppDataList)
	location =  AppData + "\\Local\\Autodesk\\Revit"
	cashedFile = System.IO.Directory.GetFiles(location, str(model_guid) +'.rvt' ,SearchOption.AllDirectories)
	cloud_local_path = next((Path.GetFullPath(file) for file in cashedFile), None)
	print(cloud_local_path)
	central_info = BasicFileInfo.Extract(cloud_local_path)
	central_version = central_info.GetDocumentVersion()
	version_guid = central_version.VersionGUID 
else:
	modelPath = doc.GetWorksharingCentralModelPath()
	model_rvtpath = ModelPathUtils.ConvertModelPathToUserVisiblePath(modelPath)
	central_info = BasicFileInfo.Extract(model_rvtpath)
	central_version = central_info.GetDocumentVersion()
	version_guid = central_version.VersionGUID 

doc_diff = doc.GetChangedElements(version_guid)
modified_elementIds = doc_diff.GetModifiedElementIds()
created_elementIds = doc_diff.GetCreatedElementIds()
deleted_elementIds = doc_diff.GetDeletedElementIds()

if all(len(x) == 0 for x in [ modified_elementIds, created_elementIds,  deleted_elementIds]):
	OUT = "File is Saved"
else:
	OUT = "File is Not Saved"
	print("modified_elementIds : " + str(len(modified_elementIds)))
	print("created_elementIds : " + str(len(created_elementIds)))
	print("deleted_elementIds : " + str(len(deleted_elementIds)))
4 Likes

Thank you for your suggestion, but the python node still reports an error.
Is this method only applicable to workshared documents?

image

The code above parses the file path from the central file.

What is the scope you want the code to work in? What have you tried to code so far?

The picture above is the result of directly pasting the program code provided by @c.poupin onto the PythonScript node intact. So my question now is, if the Document is not workshared, is it useless?
In addition, I have currently tried Document.IsModified, Document.HasAllChangesFromCentral, Document.GetChangedElements as stated in my first reply.

No you just need to implement something different to cover non-workshared situations, if that is something you’re after.

The question remains - what are you really after here in terms of scope. By “has been saved” I think ‘has the open file changed since the last version’, which means we need to refer to the previous path, two examples of this are above.

  • If a user starts a new project are you trying to catch differences to that?
  • If a user opens a cloud worksharing model are you trying to catch that?
  • If a user opens a revit server model?
  • A non-workshared model?
  • A cloud model (non-workshared)?
  • A template?
  • A family document?
  • Starts a new family?
  • Opens an IFC?

Likely there are a half dozen more options I could put in the list above plus some permutations of the various combination states (I didn’t even mention linked documents yet so we’re tripling it to start with).
I suppose “file has been saved” could also mean “is it a new document with no saved work at all”.

As I can’t see what you’re really after I can’t recommend you beyond saying ‘this might be the API method you’ll need to use’ which was previously done and another user provided a sample to boot. Give a little more scope and clarity to what you’re after and the community will certainly give back; otherwise we are playing guessing games and sadly my telepathy skills aren’t very well honed.

Sorry, I’m not quite sure yet What scope I want the code to work in…:cold_sweat:
What I want to do now is just to let Dyn automatically determine whether the current model file is in an saved state, and if not, display a reminder message to remind the user to save. (Actually there is no need to determine whether the components have been modified. However, because it fails to directly determine the archive status , so I try to determine the status through the modified component)

The sample model I used to test was not workshared, so this error message was generated. (But I don’t actually know whether there is any difference in the judgment method between workshared or other types of files😣).

Thank you for your patience and guidance.:sob:

Ok - so knowing what you want to do, perhaps some indication as to why you’re after this will clear things up?

If the ‘why’ is to ensure users are synching every so often, Dynamo is the wrong tool. Dynamo can only execute on demand, so you can’t use it to build a tool to “make sure the users sync every 15 minutes or so” as the user would have to hit the button to run the check and they’ll remember to synch just as frequently as they remember to check if they have synched.

If the ‘why’ is to ensure the document is saved before using a larger workflow, why not force the save directly? This will ensure that you’re saved without having to ask.
And if the ‘why’ is something else… well then we need to know that so we can help build the code which solves that problem.

Thank you.
Because the next step that needs to be performed is different when there is an archive and when there is no archive. So I want to display a warning message to remind/suggest the user to save(Because the subsequent actions without archiving are much more complicated).

And how would you know if there is an archive (thinking outside of Revit on this one)

Sorry, I think I may need to correct the wording above(maybe it’s not called archive?). :cold_sweat:
What I mean is to determine whether the current model has been saved.

In revit there are settings to remind the user to save at intervals, why not use this instead?

To me this workflow of checking the model has been saved is the same as just saving.

Hi,

here the code updated for No-Workshared and Workshared projects

import clr
import sys
import System
from System.Collections.Generic import List, IList, Dictionary
from System.IO import StringReader, Path, File, SearchOption 
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#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
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

local_version = Document.GetDocumentVersion(doc)
#

if doc.IsWorkshared and doc.IsModelInCloud :
	cloud_ModelPath = doc.GetCloudModelPath()
	model_guid = cloud_ModelPath.GetModelGUID().ToString()
	AppDataList = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData).split("\\")
	AppDataList.pop(-1)
	AppData = "\\".join(AppDataList)
	location =  AppData + "\\Local\\Autodesk\\Revit"
	cashedFile = System.IO.Directory.GetFiles(location, str(model_guid) +'.rvt' ,SearchOption.AllDirectories)
	cloud_local_path = next((Path.GetFullPath(file) for file in cashedFile), None)
	print(cloud_local_path)
	central_info = BasicFileInfo.Extract(cloud_local_path)
	central_version = central_info.GetDocumentVersion()
	version_guid = central_version.VersionGUID 
elif doc.IsWorkshared:
	modelPath = doc.GetWorksharingCentralModelPath()
	model_rvtpath = ModelPathUtils.ConvertModelPathToUserVisiblePath(modelPath)
	central_info = BasicFileInfo.Extract(model_rvtpath)
	central_version = central_info.GetDocumentVersion()
	version_guid = central_version.VersionGUID 
else:
	central_info = BasicFileInfo.Extract(doc.PathName)
	central_version = central_info.GetDocumentVersion()
	version_guid = central_version.VersionGUID 

doc_diff = doc.GetChangedElements(version_guid)
modified_elementIds = doc_diff.GetModifiedElementIds()
created_elementIds = doc_diff.GetCreatedElementIds()
deleted_elementIds = doc_diff.GetDeletedElementIds()

if all(len(x) == 0 for x in [ modified_elementIds, created_elementIds,  deleted_elementIds]):
	OUT = "File is Saved"
else:
	OUT = "File is Not Saved"
	print("modified_elementIds : " + str(len(modified_elementIds)))
	print("created_elementIds : " + str(len(created_elementIds)))
	print("deleted_elementIds : " + str(len(deleted_elementIds)))
3 Likes

This method worked!
Thank you very much!:sob:

1 Like