Generate connectors and set associated parameters according to sta file

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