Custom Family Type Catalog Exporter

I have noticed that not all of the Parameters nor parameter values for some of my Families were being exported when using the default Export Family Types feature in Revit. This was mostly true for Family Type Shared Parameters that are used for Labels.

Based on wanting to remedy that, I have worked through and crated a graph that allows you to pick the Parameters to export as well as doing some pre filtering for you such as parameters that don’t have a value or ones that are driven by formula.

Here is the Python node that gathers the parameter information:

#Sean Page, 2020
import clr

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument
params = []
names = []
groups = []

#Iternate each parameter in the Family
for param in doc.FamilyManager.Parameters:
	#If the Parameter if Formula Driven, no need to export it
	if param.Formula == None:
		for type in doc.FamilyManager.Types:
			#Check each type to see if the Parameter has a value, if no Type has a value no need to export
			if type.HasValue(param):
				#Check to see if the list already contains the Parameter because you only need it once
				if not params.Contains(param):
					#Add the parameter and parameter name to the output lists
					params.Add(param)
					names.Add(param.Definition.Name)
					groups.Add(param.Definition.ParameterGroup)

OUT = names,params,groups

Here is the Python for the node that gets the values based on ParameterType and Parameter StorageType.

#Sean Page, 2020
import clr

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager

clr.AddReference('System')
from System.Collections.Generic import List
from System import *
from System.IO import StreamWriter
doc = DocumentManager.Instance.CurrentDBDocument
#Preparing input from dynamo to revit
path = IN[1]
#units = doc.GetUnits().GetFormatOptions(UnitType.UT_Length).DisplayUnits
famParams = IN[0]
output = []
famTypes = doc.FamilyManager.Types
#Set up the first line of the Type Catalog with the headers
variables = "";
for param in famParams:
	pType = param.Definition.ParameterType
	pName = param.Definition.Name
	
	if pType == ParameterType.Length:
		variables += "," + pName + "##LENGTH##FEET"
	elif pType == ParameterType.Area:
		variables += "," + pName + "##AREA##SQUARE_FEET"
	elif pType == ParameterType.Volume:
		variables += "," + pName + "##VOLUME##CUBIC_FEET"
	elif pType == ParameterType.Slope:
		variables += "," + pName + "##SLOPE##SLOPE_DEGREES"
	elif pType == ParameterType.Angle:
		variables += "," + pName + "##ANGLE##DECIMAL DEGREES"
	elif pType == ParameterType.Currency:
		variables += "," + pName + "##CURRENCY##CURRENCY"
	else:
		variables += "," + pName + "##OTHER##"
#Add the headers to the final output
output.Add(variables)
#Iterate each type and get the values for each parameter ased on its Storage Type and Parameter Type
for famType in famTypes:
	typeString = String.Empty
	typeString += famType.Name
	for param in famParams:
		pType = param.Definition.ParameterType
		sType = param.StorageType
		value = String.Empty
		if sType == StorageType.Double:
			if pType == ParameterType.Angle:
				value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),DisplayUnitType.DUT_DECIMAL_DEGREES).ToString()
			elif pType == ParameterType.Slope:
				value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),DisplayUnitType.DUT_SLOPE_DEGREES).ToString()
			else:
				value = famType.AsDouble(param).ToString()
		if sType == StorageType.Integer:
			value = famType.AsInteger(param).ToString()
		if sType == StorageType.String:
			value = famType.AsString(param)
		if sType == StorageType.ElementId:
			eid = famType.AsElementId(param)
			elem = doc.GetElement(eid)
			try:
				value = elem.Name
			except:
				value = elem.get_Parameter(BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString()
		#Add each value for each parameter
		typeString += ","+value
	#Add all values for each Family Type	
	output.Add(typeString)
try:
	#Write each line to a txt file
	sw = StreamWriter(path)
	for line in output:
		sw.WriteLine(line)
	sw.Close()
	OUT = output
except:
	OUT = "Failed to Write File"

Selected Family Type Catalog.dyn (40.8 KB)

As always, thanks to @Mostafa_El_Ayoubi for Data-Shapes!

8 Likes

@SeanP This is awesome I have some families that have greatness to them but if I use the standard type export it goes crazy on import. So extremely Happy to see this! That being said I have been trying to poke into the code for a bit. I for some reason end up with a labeled _1 column which then gives me the error of to many columns on import of family by type catalog. I’m guessing it might have more to due with my family but just curious if you have seen this behavior? Im playing with the python at the moment but haven’t seen anything that could cause it yet.

image

I can’t say I have seen this behavior, but is there any way you could share the family and the Catalog export?

1 Like

@SeanP I appreciate the quick response but after a night off and a few beers it hit me this morning. Material Name… Something I don’t manage for my firm is the Material Library and someone had the Idea to name the Material “Aluminum, Anodized Black” which due to how Material parameter values are stored (can’t be formula based) the comma gets read as a comma not part of the Name string. Thus adding a column and pushing all parameter values over by on column putting ints in string positions thus causing the loads to fail. I swore after reviewing your code it couldn’t be it, but I was running out of Ideas and thought to reach out. This code your posted is a life saver!

1 Like

Thanks for circling back around on this. It looks like the default Revit export approach encapsulates values with special characters in double quotes. I will look to refactor the python to include this.

Sean truly appreciate you working to update/upkeep your code. When you say special characters do you mean ° for example? New problem is the insert side the new warning is that the parameter doesn’t exist and ° is coming in as ° in the warning dialog. I was just going to start to look into it now.

Basically any characters that may cause issues like “,” commas or " ’ " apostrophes etc. When you use the standard export from Revit family you would get “something, like it’s tomorrow” where right now this graph would just give you something, like it’s tomorrow and as you know that break its. One way would be to just wrap every value in double quotes to help mitigate that, but probably needs a more complete approach to be scalable.

You can test this with a simple family, make a couple types and a couple parameter. You’ll see if say you have a material with a comma in it, it will now have quotes.

Seat Support.txt (320 Bytes)

Gotcha that makes sense. Not that I recommend putting ",’ in most places to those in my firm just like ‘.’ in file names. (although I do always tell the new guy to name the file “Con” just for fun.) Type Catalogs are great but unforgiving in that import export process

1 Like

side question is this saving as UTF-8 or ANSI when making the TXT? Looks like UTF-8 found if you have parameters named with specials characters a quick file save as with ANSI fixes the issue.

Ok, so here is what I have come up with, and what is still not working just right.

  • I exported a Type Catalog from Revit directly and it was actually encoded as UTF-16LE

  • This export maintained special characters / letter as I expected, but broke for Parameter Names / Values with commas.

  • I refactored to encapsulate either of those values when they had a comma

  • I refactored the write to use the codecs class rather than the stream writer.

  1. This enables us to encode as we want to the UTF-16LE or any other as needed
  2. Problem is that for some reason it is hiding an invisible character at the beginning of the text file that is breaking revit.
  • This can be solved in two ways:
  1. Save As from notepad as ANSI
  2. Delete and replace the very first comma on the first line of the file.

There would probably be a way to open and replace the comma, but my first attempts saved the file back as UTF-8 or something other than what it was so I stopped thinking it may need a completely different approach.

#Sean Page, 2021
import clr

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager

clr.AddReference('System')
from System.Collections.Generic import List
from System import *
from System.IO import StreamWriter
from System.Text import Encoding
import codecs
doc = DocumentManager.Instance.CurrentDBDocument
#Preparing input from dynamo to revit
path = IN[1]
#units = doc.GetUnits().GetFormatOptions(UnitType.UT_Length).DisplayUnits
famParams = IN[0]
output = []
famTypes = doc.FamilyManager.Types
#Set up the first line of the Type Catalog with the headers
variables = ''
for param in famParams:
	pType = param.Definition.ParameterType
	pName = param.Definition.Name
	#check to see if the parameter name has a comma and encapsulate with double quoates
	if pName.Contains(','):
		pName = '"'+pName+'"'
	if pType == ParameterType.Length:
		variables += "," + pName + "##LENGTH##FEET"
	elif pType == ParameterType.Area:
		variables += "," + pName + "##AREA##SQUARE_FEET"
	elif pType == ParameterType.Volume:
		variables += "," + pName + "##VOLUME##CUBIC_FEET"
	elif pType == ParameterType.Slope:
		variables += "," + pName + "##SLOPE##SLOPE_DEGREES"
	elif pType == ParameterType.Angle:
		variables += "," + pName + "##ANGLE##DECIMAL DEGREES"
	elif pType == ParameterType.Currency:
		variables += "," + pName + "##CURRENCY##CURRENCY"
	else:
		variables += "," + pName + "##OTHER##"
#Add the headers to the final output
output.Add(variables + '\r\n')
#Iterate each type and get the values for each parameter ased on its Storage Type and Parameter Type
for famType in famTypes:
	typeString = String.Empty
	typeString += famType.Name
	for param in famParams:
		pType = param.Definition.ParameterType
		sType = param.StorageType
		value = String.Empty
		if sType == StorageType.Double:
			if pType == ParameterType.Angle:
				value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),DisplayUnitType.DUT_DECIMAL_DEGREES).ToString()
			elif pType == ParameterType.Slope:
				value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),DisplayUnitType.DUT_SLOPE_DEGREES).ToString()
			else:
				value = famType.AsDouble(param).ToString()
		if sType == StorageType.Integer:
			value = famType.AsInteger(param).ToString()
		if sType == StorageType.String:
			value = famType.AsString(param)
		if sType == StorageType.ElementId:
			eid = famType.AsElementId(param)
			elem = doc.GetElement(eid)
			try:
				value = Element.Name.__get__(elem)
			except:
				#This catches materials specifically that are "<Default>" and don't have a name
				value = String.Empty
		#Check for commas in the value and encapsulate with double quotes as needed
		if value.Contains(','):
			value = '"'+value+'"'
		#Add each value for each parameter
		typeString += ","+value
	#Add all values for each Family Type	
	output.Add(typeString + '\r\n')
outfile = codecs.open(path, 'w', encoding='utf-16-le')
try:
	#Write each line to a txt file while encoding for special characters
	for line in output:
		outfile.write(line)
	outfile.close()
	OUT = output
except:
	outfile.close()
	OUT = "Failed to Write File"

Selected Family Type Catalog_v2.dyn (37.1 KB)

1 Like