Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Handle ForgeTypeId in family config import/export for Revit 2023+ #2526

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,9 @@ def read_configs(selected_fparam_names,
fparam_shared = sparam.fparam.IsShared
if HOST_APP.is_newer_than(2022): # ParameterType deprecated in 2023
fparam_type = sparam.fparam.Definition.GetDataType()
fparam_type_str = fparam_type.TypeId
fparam_group = sparam.fparam.Definition.GetGroupTypeId().TypeId
fparam_type_str = str(fparam_type.TypeId) # Convert TypeId to string
group_type_id = sparam.fparam.Definition.GetGroupTypeId()
fparam_group = str(group_type_id.TypeId) if group_type_id else DEFAULT_PARAM_GROUP
else:
fparam_type = sparam.fparam.Definition.ParameterType
fparam_type_str = str(fparam_type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,17 +159,25 @@ def get_param_config(param_name, param_opts):
Extract parameter configurations from given dict
'''
if HOST_APP.is_newer_than(2022): # ParameterType deprecated in 2023
param_group = DB.ForgeTypeId(param_opts.get(
PARAM_SECTION_GROUP, DEFAULT_PARAM_GROUP))
param_famtype = None
param_type = DB.ForgeTypeId(
param_opts.get(PARAM_SECTION_TYPE, DEFAULT_TYPE))
if DB.Category.IsBuiltInCategory(DB.ForgeTypeId(param_opts.get(
PARAM_SECTION_TYPE, DEFAULT_TYPE
))):
param_famtype = param_opts.get(PARAM_SECTION_CAT, None)
if param_famtype:
param_famtype = revit.query.get_category(param_famtype)
try:
# Try to create ForgeTypeId from the stored string
group_type_id = param_opts.get(PARAM_SECTION_GROUP, DEFAULT_PARAM_GROUP)
param_group = DB.ForgeTypeId(group_type_id)

type_type_id = param_opts.get(PARAM_SECTION_TYPE, DEFAULT_TYPE)
param_type = DB.ForgeTypeId(type_type_id)

param_famtype = None
# Check if this is a category parameter
if type_type_id and 'autodesk.revit.category' in type_type_id.lower():
param_famtype = param_opts.get(PARAM_SECTION_CAT, None)
if param_famtype:
param_famtype = revit.query.get_category(param_famtype)
except Exception as ex:
logger.error('Error parsing ForgeTypeId: %s | Using defaults', ex)
param_group = DB.ForgeTypeId(DEFAULT_PARAM_GROUP)
param_type = DB.ForgeTypeId(DEFAULT_TYPE)
param_famtype = None
else:
# Extract parameter configurations from given dict
param_group = coreutils.get_enum_value(
Expand Down
117 changes: 117 additions & 0 deletions tests/test_family_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import pytest
from unittest.mock import MagicMock, patch
from pyrevit import DB, HOST_APP

# Import the functions to test
from extensions.pyRevitTools.extension.pyRevit.tab.Project.panel.ptools.stack.Family.pulldown.Export_Family_Config.pushbutton.script import read_configs
from extensions.pyRevitTools.extension.pyRevit.tab.Project.panel.ptools.stack.Family.pulldown.Import_Family_Config.pushbutton.script import get_param_config

def test_read_configs_revit2023():
"""Test parameter export in Revit 2023+"""
# Mock Revit objects
mock_param = MagicMock()
mock_param.Definition.Name = "TestParam"
mock_param.IsInstance = True
mock_param.IsReporting = False
mock_param.Formula = None
mock_param.IsShared = False

# Mock ForgeTypeId for Revit 2023+
mock_type_id = MagicMock()
mock_type_id.TypeId = "autodesk.spec.aec:length-1.0.0"
mock_group_id = MagicMock()
mock_group_id.TypeId = "autodesk.spec.aec:construction-1.0.0"

mock_param.Definition.GetDataType.return_value = mock_type_id
mock_param.Definition.GetGroupTypeId.return_value = mock_group_id

# Mock HOST_APP
with patch('pyrevit.HOST_APP') as mock_host:
mock_host.is_newer_than.return_value = True

# Call function
configs, _ = read_configs([mock_param.Definition.Name], include_types=False)

# Verify results
param_config = configs['parameters']['TestParam']
assert param_config['type'] == "autodesk.spec.aec:length-1.0.0"
assert param_config['group'] == "autodesk.spec.aec:construction-1.0.0"
assert param_config['instance'] == True
assert param_config['reporting'] == False

def test_get_param_config_revit2023():
"""Test parameter import in Revit 2023+"""
param_opts = {
'type': 'autodesk.spec.aec:length-1.0.0',
'group': 'autodesk.spec.aec:construction-1.0.0',
'instance': 'true',
'reporting': 'false'
}

# Mock HOST_APP
with patch('pyrevit.HOST_APP') as mock_host:
mock_host.is_newer_than.return_value = True

# Mock ForgeTypeId
with patch('pyrevit.DB.ForgeTypeId') as mock_forge_type:
# Call function
param_config = get_param_config("TestParam", param_opts)

# Verify ForgeTypeId was created with correct values
mock_forge_type.assert_any_call('autodesk.spec.aec:length-1.0.0')
mock_forge_type.assert_any_call('autodesk.spec.aec:construction-1.0.0')

# Verify param config
assert param_config.name == "TestParam"
assert param_config.isinst == True
assert param_config.isreport == False

def test_get_param_config_revit2023_with_category():
"""Test category parameter import in Revit 2023+"""
param_opts = {
'type': 'autodesk.revit.category.family:doors-1.0.0',
'group': 'autodesk.spec.aec:construction-1.0.0',
'instance': 'true',
'reporting': 'false',
'category': 'Doors'
}

# Mock HOST_APP and category query
with patch('pyrevit.HOST_APP') as mock_host, \
patch('pyrevit.query.get_category') as mock_get_cat:
mock_host.is_newer_than.return_value = True
mock_get_cat.return_value = MagicMock()

# Call function
param_config = get_param_config("TestParam", param_opts)

# Verify category was queried
mock_get_cat.assert_called_once_with('Doors')

# Verify param config has category
assert param_config.famcat is not None

def test_get_param_config_revit2023_error_handling():
"""Test error handling during parameter import in Revit 2023+"""
param_opts = {
'type': 'invalid:type-id',
'group': 'invalid:group-id',
'instance': 'true',
'reporting': 'false'
}

# Mock HOST_APP
with patch('pyrevit.HOST_APP') as mock_host, \
patch('pyrevit.script.get_logger') as mock_logger:
mock_host.is_newer_than.return_value = True

# Mock ForgeTypeId to raise exception
with patch('pyrevit.DB.ForgeTypeId', side_effect=Exception("Invalid TypeId")):
# Call function
param_config = get_param_config("TestParam", param_opts)

# Verify error was logged
mock_logger.return_value.error.assert_called_once()

# Verify fallback to defaults
assert param_config is not None # Should not return None