Can someone help me merge these two python scripts into one piece of code? I would like to use them for batch processing, thank you very much!!
00BNA0000030.rfa (416 KB)
import clr
clr.AddReference('ProtoGeometry')
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from Autodesk.DesignScript.Geometry import *
from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
import System
# Class for processing Revit elements
class RevitElementProcessor:
def __init__(self, doc):
"""
Initialize the RevitElementProcessor with a Revit document.
:param doc: The Revit document.
"""
self.doc = doc
def find_target_category(self, category_id):
"""
Find the target category based on the given category ID.
:param category_id: The ID of the category to find.
:return: The target category object.
:raises ValueError: If the category with the given ID is not found.
"""
try:
return self.doc.Settings.Categories.get_Item(category_id)
except Exception as e:
self._handle_exception(e, f"Category with ID {category_id} not found.")
def get_and_process_element_names(self, elements):
"""
Get and process the names of the elements.
:param elements: A list of Revit elements.
:return: A list of processed element names.
"""
return ["Connect " + self._process_name(element.LookupParameter("Name"))
for element in elements]
def _process_name(self, name_param):
"""
Process a single element's name parameter.
:param name_param: The name parameter of an element.
:return: The processed name.
"""
if name_param:
name = name_param.AsString()
parts = name.split('.')
if len(parts) > 1:
return parts[1]
return name
return "Unnamed"
def filter_and_modify_elements(self, elements, element_names, max_name_length=3):
"""
Filter and modify elements and their names based on the maximum name length.
:param elements: A list of Revit elements.
:param element_names: A list of element names.
:param max_name_length: The maximum length of the element names.
:return: A tuple containing two lists: filtered elements and filtered element names.
"""
return ([element for element, name in zip(elements, element_names)
if len(name) <= max_name_length],
[name for name in element_names if len(name) <= max_name_length])
def _handle_exception(self, e, error_msg):
"""
Handle exceptions by printing the error and raising a ValueError.
:param e: The exception object.
:param error_msg: The error message to raise.
:raises ValueError: The error message.
"""
print(f"Error: {e}")
raise ValueError(error_msg)
def process_elements(category_id=-2000196, max_name_length=3):
"""
Process Revit elements of a specific category.
:param category_id: The ID of the category to process (default: -2000196).
:param max_name_length: The maximum length of the element names (default: 3).
:return: A tuple containing two lists: filtered elements and filtered element names.
"""
doc = DocumentManager.Instance.CurrentDBDocument
processor = RevitElementProcessor(doc)
target_category = processor.find_target_category(category_id)
elements_of_target_category = FilteredElementCollector(doc).OfCategoryId(target_category.Id).WhereElementIsNotElementType().ToElements()
processed_element_names = processor.get_and_process_element_names(elements_of_target_category)
return processor.filter_and_modify_elements(elements_of_target_category, processed_element_names, max_name_length)
final_collector, final_element_names = process_elements()
OUT = final_collector, final_element_names
import clr
import os
import System
# Add a reference to ProtoGeometry
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# Add a reference to RevitAPI
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
# Add a reference to RevitServices
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
# Add a reference to RevitNodes
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)
# The RevitErrorHandler class is refactored to use the singleton pattern to ensure that only one instance handles errors.
class RevitErrorHandler:
_instance = None
def __new__(cls, *args, **kwargs):
"""
Create a new instance if it doesn't exist, otherwise return the existing instance.
This ensures the singleton pattern.
"""
if not cls._instance:
cls._instance = super(RevitErrorHandler, cls).__new__(cls)
return cls._instance
def __init__(self, log_path="revit_error.log"):
"""
Initialize the RevitErrorHandler with a log file path.
:param log_path: The path of the log file. Default is "revit_error.log".
"""
self.log_path = log_path
def log_error(self, error_msg):
"""
Write the error message to the log file.
:param error_msg: The error message to be written.
"""
with open(self.log_path, "a") as f:
f.write(f"{error_msg}\n")
def handle_error(self, e, operation):
"""
Construct an error message, print it, and log it.
:param e: The exception object.
:param operation: The operation during which the error occurred.
"""
error_msg = f"Error during {operation}: {str(e)}"
print(error_msg)
self.log_error(error_msg)
# Get the faces of elements
def get_faces(elements):
"""
Get the faces of the given elements.
If the input is not a list, it will be treated as a single - element list.
:param elements: The elements or a single element from which to get faces.
:return: A list of faces.
"""
result_faces = []
elements_to_process = [elements] if not isinstance(elements, list) else elements
for e in elements_to_process:
if hasattr(e, 'Faces'):
faces = e.Faces
if faces:
result_faces.extend(faces)
return result_faces
# Find circular faces from a list of faces
def find_circular_faces(faces):
"""
Find circular faces from a list of faces.
:param faces: A list of faces to check.
:return: A list of circular faces.
"""
circular_faces = []
for face in faces:
if hasattr(face, 'PerimeterCurves'):
curves = face.PerimeterCurves()
for curve in curves:
if isinstance(curve, Circle):
# If there is a circular curve among the perimeter curves of the face, then the face is circular.
circular_faces.append(face)
break
return circular_faces
# Get references of circular faces
def get_references(circular_faces):
"""
Get references of circular faces.
:param circular_faces: A list of circular faces.
:return: A list of references.
"""
references = []
for face in circular_faces:
try:
# Get the reference corresponding to the "RevitFaceReference" tag of the circular face.
reference = face.Tags.LookupTag("RevitFaceReference")
references.append(reference)
except Exception as e:
RevitErrorHandler().handle_error(e, "getting reference")
references.append(None)
return references
# Get the diameter of a circular face
def get_circular_face_diameter(face):
"""
Calculate the diameter of a circular face.
:param face: The circular face.
:return: The diameter of the circular face, or None if an error occurs.
"""
try:
curves = face.PerimeterCurves()
for curve in curves:
if isinstance(curve, Circle):
# Calculate the diameter of the circular face (diameter = 2 × radius).
return 2 * curve.Radius
except Exception as e:
RevitErrorHandler().handle_error(e, "getting diameter")
return None
# The RevitTransaction class is refactored to simplify transaction management.
class RevitTransaction:
def __init__(self, doc):
"""
Initialize the RevitTransaction with a Revit document.
:param doc: The Revit document.
"""
self.doc = doc
def __enter__(self):
"""
Ensure the transaction is started.
:return: The current instance.
"""
TransactionManager.Instance.EnsureInTransaction(self.doc)
return self
def __exit__(self, exc_type, exc_value, traceback):
"""
Complete the transaction operation.
If an exception occurred, handle the error.
"""
TransactionManager.Instance.TransactionTaskDone()
if exc_type:
RevitErrorHandler().handle_error(exc_value, "Revit transaction operation")
# Create parameters in a Revit family document
def create_parameters(param_names):
"""
Create parameters in the current Revit family document.
:param param_names: A list of parameter names to create.
:return: A list of results indicating the status of parameter creation.
"""
doc = DocumentManager.Instance.CurrentDBDocument
with RevitTransaction(doc):
param_type = ParameterType.Length
param_group = BuiltInParameterGroup.PG_GEOMETRY
created_params = []
for param_name in param_names:
existing_param = doc.FamilyManager.get_Parameter(param_name)
if existing_param:
created_params.append(f"Warning: Parameter '{param_name}' already exists.")
continue
try:
# Create a new parameter.
new_param = doc.FamilyManager.AddParameter(param_name, param_group, param_type, False)
created_params.append(f"Parameter '{param_name}' created successfully.")
except Exception as e:
RevitErrorHandler().handle_error(e, f"creating parameter {param_name}")
created_params.append(f"Warning: Parameter '{param_name}' could not be created. Error: {str(e)}")
return created_params
# Set parameter values for a specific family type
def set_parameter_value(family_type_name, parameter_name, parameter_value):
"""
Set the value of a parameter for a specific family type.
:param family_type_name: The name of the family type.
:param parameter_name: The name of the parameter.
:param parameter_value: The value to set.
"""
doc = DocumentManager.Instance.CurrentDBDocument
with RevitTransaction(doc) as _:
family_types = doc.FamilyManager.Types
family_type = next((ft for ft in family_types if ft.Name == family_type_name), None)
if family_type is None:
family_type = doc.FamilyManager.NewType(family_type_name)
try:
doc.FamilyManager.CurrentType = family_type
parameter = doc.FamilyManager.get_Parameter(parameter_name)
if parameter is None:
raise ValueError(f"Parameter {parameter_name} not found.")
# Convert the parameter value to millimeters.
parameter_value_in_mm = parameter_value / 304.8
doc.FamilyManager.Set(parameter, parameter_value_in_mm)
except Exception as e:
RevitErrorHandler().handle_error(e, f"setting parameter value {parameter_value} for parameter {parameter_name}")
# The ParameterAssociator class is refactored to simplify parameter association logic.
class ParameterAssociator:
def __init__(self, doc):
"""
Initialize the ParameterAssociator with a Revit document.
:param doc: The Revit document.
"""
self.doc = doc
def associate_parameters(self, elements, epSearch, fpSearch):
"""
Associate element parameters with family parameters.
:param elements: The elements.
:param epSearch: The names of element parameters to search for.
:param fpSearch: The names of family parameters to search for.
:return: A list of error reports.
"""
elements = [elements] if not isinstance(elements, list) else elements
epSearch = [epSearch] if not isinstance(epSearch, list) else epSearch
fpSearch = [fpSearch] if not isinstance(fpSearch, list) else fpSearch
error_report = []
for e in elements:
e = UnwrapElement(e)
with RevitTransaction(self.doc):
elemParams = e.Parameters
epNames = [ep.Definition.Name for ep in elemParams]
epDict = dict(zip(epNames, elemParams))
elemAssoc = [epDict.get(ep, "none") for ep in epSearch]
famParams = e.Document.FamilyManager.Parameters
fpNames = [fp.Definition.Name for fp in famParams]
fpDict = dict(zip(fpNames, famParams))
famAssoc = [fpDict.get(fp, "none") for fp in fpSearch]
for i, j in zip(elemAssoc, famAssoc):
try:
# Associate the element parameter with the family parameter.
e.Document.FamilyManager.AssociateElementParameterToFamilyParameter(i, j)
fpSearch.pop(0)
error_report.append("No errors")
except Exception as e:
RevitErrorHandler().handle_error(e, "parameter association")
error_report.append(f"can't assign parameter: {str(e)}")
return error_report
# Create pipe connectors based on references of circular faces
def create_pipe_connectors(references, doc):
"""
Create pipe connectors based on the references of circular faces.
:param references: A list of references of circular faces.
:param doc: The Revit document.
:return: A list of created pipe connectors.
"""
mepSystem_Types = System.Enum.GetValues(Autodesk.Revit.DB.Plumbing.PipeSystemType)
mepSystemType = mepSystem_Types[12] if len(mepSystem_Types) > 12 else mepSystem_Types[-1]
pipe_connectors = []
for ref in references:
if ref is not None:
try:
if not isinstance(mepSystemType, Autodesk.Revit.DB.Plumbing.PipeSystemType):
if isinstance(mepSystemType, int):
mepSystemType = str(mepSystemType)
mepSystemType = System.Enum.Parse(Autodesk.Revit.DB.Plumbing.PipeSystemType, mepSystemType)
# Create a pipe connector.
connector = ConnectorElement.CreatePipeConnector(doc, mepSystemType, ref)
if connector:
pipe_connectors.append(connector)
except Exception as e:
RevitErrorHandler().handle_error(e, "creating pipe connector")
return pipe_connectors
def process_elements(final_collector, final_element_names):
"""
Process the elements by performing a series of operations.
:param final_collector: The collection of elements.
:param final_element_names: The names of the elements.
:return: An error report.
"""
# Get the faces of the elements.
faces = get_faces(final_collector)
# Get the circular faces.
circular_faces = find_circular_faces(faces)
# Get the references of the circular faces.
references = get_references(circular_faces)
# Get the diameters of the circular faces (filter out cases where getting the diameter fails).
diameters = [get_circular_face_diameter(face) for face in circular_faces if get_circular_face_diameter(face) is not None]
doc = DocumentManager.Instance.CurrentDBDocument
if not doc.IsFamilyDocument:
print("The current document is not a family document. Please ensure that this operation is run in a family document.")
raise ValueError("The document is not a family document.")
# Create pipe connectors.
pipe_connectors = create_pipe_connectors(references, doc)
# Create parameters and get the results.
created_params_result = create_parameters(final_element_names)
# Set the value for each parameter.
for i in range(len(final_element_names)):
set_parameter_value(doc.Title, final_element_names[i], diameters[i])
epSearch = ["Diameter"]
fpSearch = final_element_names
parameter_associator = ParameterAssociator(doc)
# Associate parameters and get the error report.
error_report = parameter_associator.associate_parameters(pipe_connectors, epSearch, fpSearch)
return error_report
# Get the input data.
final_collector = IN[0]
final_element_names = IN[1]
# Call the function to process the data.
error_report = process_elements(final_collector, final_element_names)
# Output the result.
OUT = error_report