Create filter rule without parameter element

Hi,

I’m trying to create a filter from excel.

The only issue I have is with creating the filter rule:
To use “ByRuleType” node, I need an element to create the filter rule, I don’t want to create one element for each category to create my filter.
I could link a fake revit link to get the necessary element, but I’m struggling with my category list as well to make the node “getFromLinkFile” working (from steamNodes package).
And the node “CreateRule” don’t work.
Is there a way to get the “parameter” list via python instead of by an element?

Unfortunately, I’m still working on revit 2018 at the moment (my project has started in 2018 and we still using 2018. Don’t tell me it’s a shame, I know :roll_eyes:), and I can’t install orchid package to use create rule.

If someone has a magic solution for me…

Regards,

The parameter itself has to be the specific parameter element so Revit knows precisely what it’s looking for. The only way that parameter element exists is if it’s assigned to an element in your project. So getting the parameter from an element is really the only way unless you know the GUID, and even then, it’s still much easier to use the element than parse the whole document for a specific parameter. I think even BuiltIn parameters need to come from the specific object type being filtered to work but I may be wrong there.

All that being said, the workflow here is to use the SchedulableParameters to create a filter. You don’t actually need an element to be placed, you just need the parameter to exist.

You can call a BuiltInParameter without retrieving it from an element, if that’s what you mean. I’ve been constructing my ParameterValueProviders by just giving it the BuiltInParameter Id.
As an example - provider = ParameterValueProvider(ElementId(.SYMBOL_NAME_PARAM)) works just fine.

Edit: Just out of curiosity I ran a quick speed test, creating a value provider using a BuiltIn is a blazing 0.002 seconds faster.

I was specifically talking with respect to filters in this case, since they have to be from the acceptable list of SchedulableParameters. I don’t believe there’s a way to go from BuiltIn to Schedulable.

I’m not sure I’m following, as I can’t find SchedulableParameters in the API. Are you talking about getting instance and/or shared parameters?

The op is trying to create a ParameterFilterElement for the Description parameter, yeah?

provider = ParameterValueProvider(ElementId(BuiltInParameter.OST_ALL_MODEL_DESCRIPTION))
str_filter = FilterStringRule(provider, FilterStringEquals(), *your parameter value here*, True)
elem_filter = ElementParameterFilter(str_filter)
param_filter = ParameterFilterElement(doc, "Name", *your ICollection of categories*, elem_filter)

Should work just fine

Edit:
I’m writing out a more complete solution atm, but I think maybe I get what you mean. Are you talking about going from a string of the parameter name to a BuiltIn?
There’s a round about way to do it by turning the enumeration into a dictionary.
BIPdic = {i.value__ : i for i in BuiltInParameter.GetValues(BuiltInParameter)}

1 Like

Wow, my bad. I misread and thought they were looking at schedule filters. Thanks for pointing that out.

Yes, you are correct, you can use the BuiltIn parameter as-is without an element. You just need the parameter.

Aaah, okay this makes sense. Haha
Edit:
Removed a bunch of stuff because I realized I also misread the original post. Whoops lol
Getting a parameter list could be complicated, particularly if an element doesn’t already exist. We can access BuiltInParameters, but if it’s any sort of user made (instance/shared) parameter, save possibly project parameters, you’re probably out of luck. Basically, if you can find the parameter in this list: BuiltInParameter Enumeration (I’m sorry in advance this isn’t particularly fun to hunt through) we can probably do something in Python.

Hello,
an solution using
ParameterFilterUtilities.GetFilterableParametersInCommon()

import clr
import sys
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

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

#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)

	
def get_ParameterId_byName(lst_paraIds, para_name):
	dictBip = {ElementId(bip) : bip for bip in System.Enum.GetValues(BuiltInParameter)}
	for pId in lst_paraIds:
		if doc.GetElement(pId) is not None and doc.GetElement(pId).Name == para_name:
			return pId
		elif pId in dictBip and DB.LabelUtils.GetLabelFor(dictBip.get(pId)) == para_name:
			return pId
		else: pass
	return ElementId.InvalidElementId
				

toList = lambda x : x if hasattr(x, '__iter__') else [x]

#Preparing input from dynamo to revit
categories = toList(UnwrapElement(IN[0]))
lstCatIds = List[ElementId]([c.Id for c in categories])
parameter_Name = IN[1]
parameter_value = IN[2]
filter_Name = IN[3]

TransactionManager.Instance.EnsureInTransaction(doc)
lstParameterIds = DB.ParameterFilterUtilities.GetFilterableParametersInCommon(doc,lstCatIds)
#
paraId = get_ParameterId_byName(lstParameterIds, parameter_Name)
if paraId != ElementId.InvalidElementId:
	rule = ParameterFilterRuleFactory.CreateEqualsRule(paraId, parameter_value, True)
	if sdkNumber > 2018:
		elementFilter = ElementParameterFilter(rule)
		filter = DB.ParameterFilterElement.Create(doc,filter_Name, lstCatIds, elementFilter)
	else:
		lstRule = List[FilterRule]([rule])
		filter = DB.ParameterFilterElement.Create(doc,filter_Name, lstCatIds, lstRule)

TransactionManager.Instance.TransactionTaskDone()

OUT = paraId
2 Likes

I have looked at your answer and I haven’t been able to implement it in my code.

To explain more what I’m trying to do :

  • I would like to create multiple filter (from excel)
  • by selecting a category, I would like to retrieve the list of instances and type parameters available
  • by selecting multiple categories, I will be able to get only the common parameter

I hope this screenshot will be better to explain my goal.

Regards,

Could you perhaps share an example excel file?

What is the end product you are trying to create? A view filter like what you have shown in that image?

Hi Stewart.

I can share my Excel file, of course.

Here enclosed is the snapshot of the excel file, and a simplified version of my dynamo script.

Everything is working fine, except the “filterBy” list, because I have to select the relevant element to get the relevant parameter matching with the categories.
If I could get the parameter available for each category (and not the parameter name), that would be awesome.
I’m more than happy to remove these nodes by a python script, but I don’t have enough knowledge to create this one.


@c.poupin Had you on the right track. I took the time to add in the ability to input different FilterRule types - As it stands they will always be case sensitive if the parameter value is a string.
I also set it up to expect input how I’m assuming you’ll be handing it the information, based on your image.

I tested it out a bit, but not extensively, seemed to work just fine when I input different rule types, and values.
It does expect a string that matches one of these for the rule type, just FYI.
image

import clr
import sys
import System

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

#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)

	
def get_ParameterId_byName(lst_paraIds, para_name):
	dictBip = {ElementId(bip) : bip for bip in System.Enum.GetValues(BuiltInParameter)}
	for pId in lst_paraIds:
		if doc.GetElement(pId) is not None and doc.GetElement(pId).Name == para_name:
			return pId
		elif pId in dictBip and DB.LabelUtils.GetLabelFor(dictBip.get(pId)) == para_name:
			return pId
		else: pass
	return ElementId.InvalidElementId

def create_rule(type_name, para_id, value):
	if type_name == 'BeginsWith':
		return DB.ParameterFilterRuleFactory.CreateBeginsWithRule(para_id, value, True)
	elif type_name == 'Contains':
		return DB.ParameterFilterRuleFactory.CreateContainsRule(para_id, value, True)
	elif type_name == 'EndsWith':
		return DB.ParameterFilterRuleFactory.CreateEndsWithRule(para_id, value, True)
	elif type_name == 'Equals':
		if isinstance(value, str):
		    return DB.ParameterFilterRuleFactory.CreateEqualsRule(para_id, value, True)
		elif isinstance(value, float):
		    return DB.ParameterFilterRuleFactory.CreateEqualsRule(para_id, value, 1e-10)
		else:
		    return DB.ParameterFilterRuleFactory.CreateEqualsRule(para_id, value)
	elif type_name == 'Greater':
		if isinstance(value, str):
		    return DB.ParameterFilterRuleFactory.CreateGreaterRule(para_id, value, True)
		elif isinstance(value, float):
		    return DB.ParameterFilterRuleFactory.CreateGreaterRule(para_id, value, 1e-10)
		else:
		    return DB.ParameterFilterRuleFactory.CreateGreaterRule(para_id, value)
	elif type_name == 'GreaterOrEqual':
		if isinstance(value, str):
		    return DB.ParameterFilterRuleFactory.CreateGreaterOrEqualRule(para_id, value, True)
		elif isinstance(value, float):
		    return DB.ParameterFilterRuleFactory.CreateGreaterOrEqualRule(para_id, value, 1e-10)
		else:
		    return DB.ParameterFilterRuleFactory.CreateGreaterOrEqualRule(para_id, value)
	elif type_name == 'Less':
		if isinstance(value, str):
		    return DB.ParameterFilterRuleFactory.CreateLessRule(para_id, value, True)
		elif isinstance(value, float):
		    return DB.ParameterFilterRuleFactory.CreateLessRule(para_id, value, 1e-10)
		else:
		    return DB.ParameterFilterRuleFactory.CreateLessRule(para_id, value)
	elif type_name == 'LessOrEqual':
		if isinstance(value, str):
		    return DB.ParameterFilterRuleFactory.CreateLessOrEqualRule(para_id, value, True)
		elif isinstance(value, float):
		    return DB.ParameterFilterRuleFactory.CreateLessOrEqualRule(para_id, value, 1e-10)
		else:
		    return DB.ParameterFilterRuleFactory.CreateLessOrEqualRule(para_id, value)
	elif type_name == 'NotBeginsWith':
		return DB.ParameterFilterRuleFactory.CreateNotBeginsWithRule(para_id, value, True)
	elif type_name == 'NotContains':
		return DB.ParameterFilterRuleFactory.CreateNotContainsRule(para_id, value, True)
	elif type_name == 'NotEndsWith':
		return DB.ParameterFilterRuleFactory.CreateNotEndsWithRule(para_id, value, True)
	elif type_name == 'NotEquals':
		if isinstance(value, str):
		    return DB.ParameterFilterRuleFactory.CreateNotEqualsRule(para_id, value, True)
		elif isinstance(value, float):
		    return DB.ParameterFilterRuleFactory.CreateNotEqualsRule(para_id, value, 1e-10)
		else:
		    return DB.ParameterFilterRuleFactory.CreateNotEqualsRule(para_id, value)

toList = lambda x : x if hasattr(x, '__iter__') else [x]

def create_filter(grouped_inputs):
	ot = []
	for f_name, cats, f_type, p_value, p_name in grouped_inputs:
		cats = toList(cats)
		cats = [UnwrapElement(x) for x in cats]
		lstCatIds = List[ElementId]([c.Id for c in cats])
		lstParameterIds = DB.ParameterFilterUtilities.GetFilterableParametersInCommon(doc,lstCatIds)
		paraId = get_ParameterId_byName(lstParameterIds, p_name)
		if paraId != ElementId.InvalidElementId:
			rule = create_rule(f_type, paraId, p_value)
			if sdkNumber > 2018:
				elementFilter = ElementParameterFilter(rule)
				filter = DB.ParameterFilterElement.Create(doc, f_name, lstCatIds, elementFilter)
				ot.append(filter)
			else:
				lstRule = List[FilterRule]([rule])
				filter = DB.ParameterFilterElement.Create(doc, f_name, lstCatIds, lstRule)
				ot.append(filter)
	return ot

#Preparing input from dynamo to revit
filter_name = IN[0]
categories = IN[1]
filter_type = IN[2]
parameter_value = IN[3]
parameter_name = IN[4]

input = zip(filter_name, categories, filter_type, parameter_value, parameter_name)

TransactionManager.Instance.EnsureInTransaction(doc)
out = create_filter(input)
TransactionManager.Instance.TransactionTaskDone()

OUT = out

2 Likes

Hi @stewart.skyler,

Thank you so much for this python script. It is working perfectly.

Yes the FilterRuleType is case sensitive. But I’ve created a validation list in my Excel, and I also created a simple custom node, see bellow.

I’ve just got an error when I tried: even if there is one filter to create, the entry needs to be a list.

Thanks again,
Select Rule Type Input.dyf (25.1 KB)

You could probably fix that by adding this function with the others

def to_list(obj1):
    if hasattr(obj1,"__iter__"): 
        return obj1
    else:
        return [obj1]

and then changing
filter_type = IN[2]
to
filter_type = to_list(IN[2])

@c.poupin, I’m running this script on Revit 2024, and this line below is not working anymore:

If I run this script on CPython3, I’ve got the following error:

SystemError: error return without exception set"

If I run this script on IronPython2, I’ve got the following error:

Unable to cast object of type 'Autodesk.Revit.DB.ElementId' to type 'System.String'

Any idea how I can update my script to make it work again?

I might have also need to update create_rule function, as ParameterFilterRuleFactory. CreateEqualsRule(ElementId, String, Boolean) is now obsolete.

Hi,
work fine with IronPython (2 and 3)

For CPython3, the error is frankly not revealing

if you want to use CPython3, use the new property “Value” as workaround

or remove InvalidElementId from dictionnary

1 Like

After some research, there is a bug in PythonNet when trying to get the hash of the integer -1 (System.Int32)

therefore, impossible to create a pure python dictionary that contains this object as a key


import clr
import System
from System.Collections import IList, ArrayList
from System.Collections.Generic import Dictionary

try:
    a = System.Int32(-1)
    test1 = hash(a)
    result1 = 'SUCCES'
except Exception as ex:
    result1 = repr(ex)

try:
    a = System.Int32(-1)
    test2 = a.GetHashCode()
    result2 = 'SUCCES'
except Exception as ex:
    result2 = repr(ex)

try:
    test3 = {System.Int32(x) : str(x) for x in range(-10, 10)}
    result3 = 'SUCCES'
except Exception as ex:
    result3 = repr(ex)

try:
    test4 = Dictionary[System.Int32, str]()
    for x in range(-10, 10):
        test4.Add(System.Int32(x),str(x) )
    result4 = 'SUCCES'
except Exception as ex:
    result4 = repr(ex)

OUT = result1, result2, result3, result4
1 Like