Transactions and the Family Manager


#1

BDDIV-SSH-XX-XX-DY-BI-0150-S0-P62_FamilyTypesMonitorByExcel_ReinardtBronkhorst.dyn (91.6 KB)

I am having trouble managing transactions and using the family manager. When I run the script, I notice that each time I try to open the family manager it fails (excepts). After I remove the Try loop I get the following error:
1
I am trying to do the following:

  1. Load families into a project if they are not present

  2. Check the family category against the stored Excel value. I want to use the family manager for this even though I can achieve this with some other code.

  3. Create the required family types. An issue arises when all the types of a family in the project has been deleted as there is no type to duplicate. I also want to use the family manager to create a new type.

Is it possible for anyone to look at the way in which I am using the transaction manager and tell me where I am going wrong? Initially I had all the code in one script, but I have now broken it up into separate nodes to try to isolate my mistake. Please find attached the .dyn file. I also append the code for the two nodes.

Node 1:

#2019
#Paul.Storm@sshic.com, https://www.sshic.com
import clr 
import sys
clr.AddReference("ProtoGeometry")
from Autodesk.DesignScript.Geometry import *
import math
sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices") # Import DocumentManager
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
import traceback
clr.AddReference('RevitAPI') #import clr
clr.AddReference('RevitAPIUI')
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Plumbing import *
from Autodesk.Revit.UI import *
clr.ImportExtensions(Revit.Elements)
import System 
clr.AddReference("DSCoreNodes")
from DSCore import *
doc = DocumentManager.Instance.CurrentDBDocument #THE CURRENT DOCUMENT
xCategoryNames = IN[0] # EXCEL INPUT - CATEGORY NAME
xFamilyNames = IN[1] # EXCEL INPUT - FAMILY NAME
xTypeNames = IN[2] # EXCEL INPUT - TYPE NAMES
familyPath = IN[3] # INPUT - THE PATH TO THE FAMILY LIBRARY
rCategories = [] # EXCEL REPORT COMMENT - CATEGORY - IS EXCEL CATEGORY VALID REVIT CATEGORY?
rFamilyCategories = [] # EXCEL REPORT COMMENT - FAMILY CATEGORY - [(EXCEL CATEGORY = FAMILY CATEGORY)] IF NOT, DON'T CREATE TYPES.
rFamilyNames = [] # EXCEL REPORT COMMENT - FAMILY NAME - [(IN PROJECT?) (LOADED?) (IN LIBRARY?)]
rTypeNames = [] # EXCEL REPORT COMMENT - TYPE NAMES -
oCategories = [] # OUT VALUE - CATEGORY
oFamilies = [] # OUT VALUE - FAMILY
oFamilyTypes = [] # OUT VALUE - FAMILY TYPES
pFamilies = FilteredElementCollector(doc).OfClass(Family) # COLLECT ALL THE FAMILIES IN THE PROJECT
t1 = Transaction(doc, 'This is my new transaction')
for i, xFamilyName in enumerate(xFamilyNames, 0): # FOR ALL THE EXCEL FAMILIES
	foundFamily = False # STATE THAT NNO MATCH FOR THE EXCEL FMAILY HAS YET BEEN FOUND IN THE PROJECT
	for pFamily in pFamilies: # FOR EACH FAMILY IN THE PROJECT
		if xFamilyName == pFamily.Name: # IF THE EXCEL FAMILY EXISTS IN THE PROJECT
			foundFamily = True # STATE THAT THE EXCEL FAMILY WAS FOUND INT HE PROJECT
			rFamilyNames.append("OK") # REPORT THAT THE EXCEL FAMILY IS IN THE PROJECT
			oFamilies.append(pFamily) # OUTPUT THE FAMILY
			break # STOP LOOKING
	if foundFamily == False: # IF THE EXCEL FAMILY WAS NOT FOUND IN THE PROJECT
		booleans = [] # BOOLEAN TO INDICATE WHETHER A FAMILY WAS SUCCESSFULLY LOADED
		familyPath1 = familyPath + xFamilyName + ".rfa" # CONCATENATE THE FILE PATH TO THE INTENDED FAMILY
		
		#AM I CODING THE TRANSACTIONAL PART CORRECT? THE NODE THAT FOLLOWS THIS ONE INFORMS ME THAT A TRANSACTION IS ALREADY OPEN (DOES THIS MEAN THIS TRANSACTION NEVER CLOSES)? 
		TransactionManager.Instance.EnsureInTransaction(doc) # INITIATE THE TRANSACTION
		try: # TRY CATCH ERRORS
			doc.LoadFamily(familyPath1) # TRY TO LOAD THE INTENDED FAMILY
			booleans.append(True) #IF IT DID NOT GENERATE AN ERROR, STATE THAT THE FAMILY WAS LOADED
		except: # IF THE FAMILY WAS NOT LOADED
			booleans.append(False) # STATE THAT THE FAMILY WAS NOT IN THE FOLDER (THE SYSTEM OBJECT SHOULD BE USED TO CHECK WHETHER A FILE IS THERE OR NOT. THIS IS RUBBISH AND ONLY INDICATES THAT A FAMILY COULD NOT BE LOADED FOR WHATEVER REASON)
			rFamilyNames.append("ERROR - Family not loaded and not in library") # STATE THAT THE FAMILY HAD TO BE LOADED BUT DID NOT EXIST IN THE LIBRARY
			oFamilies.append(None) # APPEND A NULL VLAUE FOR THE FAMILY
		TransactionManager.Instance.TransactionTaskDone() #CONCLUDE THE TRANSACTION
		
		families = FilteredElementCollector(doc).OfClass(Family).ToElements() #COLLECT ALL THE FAMILIES IN THE PROJECT AGAIN TO SELECT THE ONE THAT WAS POSSIBLE ADDED
		for family in families: # FOR EACH OF THE FAMILIES IN THE PROJECT
			if family.Name == xFamilyName: # IF THE EXCEL FAMILY WAS FOUND
				rFamilyNames.append("OK") # STATE THAT THE FAMILY HAD TO BE LOADED
				oFamilies.append(family) # OUTPUT THE FAMILY
				typelist = list() # DEVLARE A LIST OF FAMILY TYPES
				for famtypeid in family.GetFamilySymbolIds(): # COLLECT ALL THE FAMILY TYPES
					typelist.append(doc.GetElement(famtypeid).ToDSType(True))#THIS MAKES THE FAMILY SHOW UP IN THE PROJECT BROWSER. IT IS NOT VISIBLE OTHERWISE.
OUT = xCategoryNames, rCategories, rFamilyCategories, oCategories, xFamilyNames, rFamilyNames, oFamilies, xTypeNames, rTypeNames, oFamilyTypes

Node 2:

#2019
#Paul.Storm@sshic.com, https://www.sshic.com
import clr 
import sys
clr.AddReference("ProtoGeometry")
from Autodesk.DesignScript.Geometry import *
import math
sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices") # Import DocumentManager
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
import traceback
clr.AddReference('RevitAPI') #import clr
clr.AddReference('RevitAPIUI')
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Plumbing import *
from Autodesk.Revit.UI import *
clr.ImportExtensions(Revit.Elements)
import System 
clr.AddReference("DSCoreNodes")
from DSCore import *
doc = DocumentManager.Instance.CurrentDBDocument
xCategoryNames = IN[0]				# EXCEL INPUT - CATEGORY NAME
rCategories = IN[1]					# EXCEL REPORT COMMENT - CATEGORY - IS EXCEL CATEGORY VALID REVIT CATEGORY?
rFamilyCategories = IN[2] 			# EXCEL REPORT COMMENT - FAMILY CATEGORY - [(EXCEL CATEGORY = FAMILY CATEGORY)] IF NOT, DON'T CREATE TYPES.
oCategories = IN[3] 				# OUT VALUE - CATEGORY
xFamilyNames = IN[4] 				# EXCEL INPUT - FAMILY NAME 
rFamilyNames = IN[5] 				# EXCEL REPORT COMMENT - FAMILY NAME - [(IN PROJECT?) (LOADED?) (IN LIBRARY?)]  
oFamilies = UnwrapElement(IN[6]) 	# OUT VALUE - FAMILY
xTypeNames = IN[7]					# EXCEL INPUT - TYPE NAMES
rTypeNames = IN[8]					# EXCEL REPORT COMMENT - TYPE NAMES - 
oFamilyTypes = UnwrapElement(IN[9])	# OUT VALUE - FAMILY TYPES
oBuiltInCategories = [] #BUILTIN-CATEGORIES THAT CORRESPOND TO EXCEL INPUT NAME
valid_pCategories = [] #ALL CATEGORIES THAT MAP TO BUILT-IN CATEGORIES
valid_pBuiltInCategories = [] #THE BUILT-IN CATEGORIES THAT CORRESPOND TO valid_pCategories

pBuiltInCategories = System.Enum.GetValues(BuiltInCategory) #ALL BUILT-IN CATEGORIES IN THE PROJECT
for pBuiltInCategory in pBuiltInCategories: #FOR EACH BUILT-IN CATEGORY
	try:# TRY CATCH ERROR
		valid_pCategories.append(Revit.Elements.Category.ById(ElementId(pBuiltInCategory).IntegerValue)) # IF BUILTINPARAMETER MAPS TO A CATEGORY, RECORD THE CATEGORY
		valid_pBuiltInCategories.append(pBuiltInCategory) #AND RECORD THE CORRESPONDING BUILT-IN CATEGORY
	except: # THE THE BUILT-IN PARAMETER DOES NOT MAP TO A CATEGORY
		pass # DO NOTHING

for xCategoryName in xCategoryNames: # FOR EACH EXCEL CATEGORY NAME
	found_xCategoryName = False # STATE THAT A MATCH FOR THE EXCEL CATEGORY HAS NOT YET BEEN FOUND IN THE PROJECT
	for i, valid_pCategory in enumerate(valid_pCategories, 0): # FOR EACH CATEGORY IN THE PROJECT
		if valid_pCategory.Name == xCategoryName: # IF THE EXCEL CATEGORY NAME CORRESPONDS TO THE PROJECT CATEGORY NAME
			found_xCategoryName = True # STATE THAT A MATCH FOR THE EXCEL CATEGORY HAS BEEN FOUND IN THE PROJECT
			rCategories.append("OK") # REPORT THAT THE EXCEL CAEGORY WAS FOUND IN THE PROJECT
			oCategories.append(valid_pCategory) # OUTPUT THE CATEGORY
			oBuiltInCategories.append(valid_pBuiltInCategories[i]) # OUTPUT THE BUILT-IN CATEGORY
			break #STOP LOOKING
	if found_xCategoryName == False: # IF THERE WAS NO MATCH FOR THE EXCEL CATEGORY IN THE PROJECT
		rCategories.append("ERROR - Category does not exist in project") # REPORT THAT THE EXCEL CATEGORY WAS NOT IN THE PROJECT I.E. IS INVALID
		oCategories.append(None) # OUTPUT A NULL CATEGORY
		oBuiltInCategories.append(None) # OUTPUT A NULL BUILTIN-CATEGORY

for i, oFamily in enumerate(oFamilies, 0):
	if oFamily == None:
		rFamilyCategories.append("ERROR - Family is not valid therefore category not evaluated")
		oCategories[i] = None
	else:
	
		#AM I CODING THE TRANSACTIONAL PART CORRECT? 
		TransactionManager.Instance.EnsureInTransaction(doc) # INITIATE THE TRANSACTION
		try: # TRY CATCH ERRORS
			famDoc = doc.EditFamily(oFamily)
			if xCategoryNames[i] == famDoc.OwnerFamily.FamilyCategory.Name:
				rFamilyCategories.append("OK")
			else:
				rFamilyCategories.append("ERROR - Family category does not correspond to what is listed in Excel")	
		except: # IF THE FAMILY WAS NOT LOADED
			rFamilyCategories.append("ERROR - Family category could not be retreived") # STATE THAT THE FAMILY HAD TO BE LOADED BUT DID NOT EXIST IN THE LIBRARY
		TransactionManager.Instance.TransactionTaskDone() #CONCLUDE THE TRANSACTION

OUT = xCategoryNames, rCategories, rFamilyCategories, oCategories, xFamilyNames, rFamilyNames, oFamilies, xTypeNames, rTypeNames, oFamilyTypes

Any help would be greatly appreciated.

Kind Regards,

Paul Storm


#2

This seems to have sorted the issue:

        #AM I CODING THE TRANSACTIONAL PART CORRECT? 
		#TransactionManager.Instance.EnsureInTransaction(doc) # INITIATE THE TRANSACTION
		TransactionManager.Instance.ForceCloseTransaction()
		try: # TRY CATCH ERRORS
			famDoc = doc.EditFamily(oFamily)
			if xCategoryNames[i] == famDoc.OwnerFamily.FamilyCategory.Name:
				rFamilyCategories.append("OK")
			else:
				rFamilyCategories.append("ERROR - Family category does not correspond to what is listed in Excel")	
		except: # IF THE FAMILY WAS NOT LOADED
			rFamilyCategories.append("ERROR - Family category could not be retreived") # STATE THAT THE FAMILY HAD TO BE LOADED BUT DID NOT EXIST IN THE LIBRARY
		#TransactionManager.Instance.TransactionTaskDone() #CONCLUDE THE TRANSACTION

#3

A couple things here.

  1. In node one you set up t1 as a transaction, but then you never use it. If you want to manually use a transaction like this, which can be good if you need to give the user the ability to “undo” parts of the graph, then you have to Start it, Commit the changes, and then Dispose of the transaction after you have defined it.

t1 = Transaction(doc, ‘This is my new transaction’)
t1.Start()
“here is where you do stuff”
t1.Commit()
t1.Dispose()
“clear your ram”

In your second post, it would probably still be a good idea to do the family editing within a transaction by placing the ForceCloseTransaction() line above the EnsureInTransaction, but as you are finding out Dynamo and Revit do a pretty good job of creating transactions when needed.


#4

Thanks Sean, There are also sub transactions that can be used in the same way.
http://www.revitapidocs.com/2018.1/801e5f17-cab0-044d-835c-a39592374f89.htm
I will certainly use force close transaction from now on as default coding. Thank you for your time. Kind Regards, Paul.