Open Families from a Directory, Purge a Category, Load and Save

The following graph will load a family from a directory, delete all unused Family and Family Types of a Category, then Save and/or Load the family into the current project.

Here is the core code in the “Open Document & Clean” node:

import System
import clr
clr.AddReference("RevitNodes")
import Revit
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
clr.AddReference("RevitAPI")
clr.AddReference('DSCoreNodes')
from DSCore import *
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)

currDoc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

paths = IN[0]
if not isinstance(paths, list): paths = [paths]
cats = List.Flatten([IN[1]],1)

save = IN[2]
load = IN[3]

class FamilyOption(IFamilyLoadOptions):
    def OnFamilyFound(self, familyInUse, overwriteParameterValues):
        overwriteParameterValues = True
        return True

    def OnSharedFamilyFound(
            self, sharedFamily, familyInUse, source, overwriteParameterValues):
        source = FamilySource.Family
        overwriteParameterValues = True
        return True

delIds = []
errorReport = None
try:
	for path in paths:
		doc = app.OpenDocumentFile(path)
		for cat in cats:
			trans = Transaction(doc,'del')
			trans.Start()

			allElements = (FilteredElementCollector(doc).OfClass(FamilySymbol).OfCategory(System.Enum.ToObject(BuiltInCategory, cat.Id)).ToElements())
			allFamilyIds = [x.Family.Id for x in allElements]
			allTypeIds = [x.Id for x in allElements]
			
			placedElements = (FilteredElementCollector(doc).OfClass(FamilyInstance).OfCategory(System.Enum.ToObject(BuiltInCategory, cat.Id)).WhereElementIsNotElementType().ToElements())
			placedElementTypeIds = set([x.get_Parameter(BuiltInParameter.ELEM_TYPE_PARAM).AsElementId() for x in placedElements])
			placedElementTypes = [doc.GetElement(x) for x in placedElementTypeIds]
			placedFamilyIds = [x.Family.Id for x in placedElementTypes]
			
			diffTypeIds = list(set(allTypeIds).difference(placedElementTypeIds))
			diffFamilyIds = list(set(allFamilyIds).difference(placedFamilyIds))

			dels = List.Join(diffTypeIds,diffFamilyIds)
			delIds.append(dels)

			for id in dels:
				doc.Delete(id)
			
			trans.Commit()
			trans.Dispose()
			
		if load:
			doc.LoadFamily(currDoc,FamilyOption())
		
		if save:
			opt = SaveAsOptions()
			opt.OverwriteExistingFile = True
			opt.Compact = True
			opt.MaximumBackups = 1
			doc.SaveAs(path,opt)	
			doc.Close(False)
		else:
			doc.Close(False)
			
except:
	import traceback
	errorReport = traceback.format_exc()

if errorReport is None:
    OUT = delIds
else:
    OUT = errorReport

Thanks go out to @erfajo, @jacob.small, @Konrad_K_Sobon and others!

15 Likes

Hi Sean,

I’m not expert on the python code.
I tried your script but I don’t have any result.
The items of the Deleted Element Ids list are always Empty List (the category is correct)

Can you help me to understand why?

Regards

Mirko

The result will be an empty list if no Elements were deleted from the family. If you notice in my original post I too have some empty list results. Are you sure there are Specialty Equipment families nested that aren’t placed in the families?

Yes I’m sure :frowning:

Hi Sean,

Pretty new to dynamo & python scripting here.

Just getting errors as shown on my screenshot when I run the node with the script given above.

Any solution suggested?

It looks like the node feeding input 0 is sending a document, but it should be sending a list of paths. Then the python uses the paths to open the documents.