mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +00:00
Resolve nested complex quantity paths in the selector (#2041)
get_element_value could not reach the members of an IfcPhysicalComplexQuantity (or IfcComplexProperty) by their natural path. util.element expands a complex quantity into a dict whose nested members live under a "properties" sub-dict, but the selector's dict navigation only looked at the top level, so "Qto_Custom.Layer1.Width" returned None and IfcCsv exported nothing for it. Only the internal "Qto_Custom.Layer1.properties.Width" path worked. When a key is not a direct member of the value dict, descend into its "properties" sub-dict so nested quantities/properties resolve with the natural "Set.Complex.Nested" path. Direct keys still take priority, so the explicit ".properties." path stays backward compatible and the regex branch is untouched. Verified: Qto_Custom.Layer1.Width -> 0.1 and Layer1.Height -> 2.5 (were None), the sibling simple NetArea still resolves, the legacy .properties. path still works, and IfcCsv now exports the nested value. test_selector.py: 38 passed (adds test_selecting_a_nested_complex_quantity). Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
committed by
Massimo Fabbro
parent
d63a99b70c
commit
21122c0d28
@@ -540,8 +540,15 @@ def _get_element_value(element: ifcopenshell.entity_instance, keys: list[str]) -
|
||||
value = results or None
|
||||
if value and len(value) == 1:
|
||||
value = value[0]
|
||||
elif key in value:
|
||||
value = value[key]
|
||||
else:
|
||||
value = value.get(key, None)
|
||||
# A nested complex quantity/property (IfcPhysicalComplexQuantity /
|
||||
# IfcComplexProperty) is represented as a dict whose nested members
|
||||
# live under a "properties" sub-dict. Descend into it so that nested
|
||||
# values are reachable with the natural "Qto.Complex.Nested" path.
|
||||
subprops = value.get("properties")
|
||||
value = subprops.get(key, None) if isinstance(subprops, dict) else None
|
||||
elif isinstance(value, (list, tuple, set)): # If we use regex
|
||||
if isinstance(key, str) and key.isnumeric():
|
||||
try:
|
||||
|
||||
@@ -195,6 +195,37 @@ class TestGetElementValue(test.bootstrap.IFC4):
|
||||
assert subject.get_element_value(element, "/Pset_.*Common/.Status") == ["New"]
|
||||
assert subject.get_element_value(element, "/Pset_.*Common/.Status.0") == "New"
|
||||
|
||||
def test_selecting_a_nested_complex_quantity(self):
|
||||
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
|
||||
complex_quantity = self.file.create_entity(
|
||||
"IfcPhysicalComplexQuantity",
|
||||
Name="Layer1",
|
||||
Discrimination="layer",
|
||||
HasQuantities=[
|
||||
self.file.create_entity("IfcQuantityLength", Name="Width", LengthValue=0.1),
|
||||
self.file.create_entity("IfcQuantityLength", Name="Height", LengthValue=2.5),
|
||||
],
|
||||
)
|
||||
quantity = self.file.create_entity(
|
||||
"IfcElementQuantity",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
Name="Qto_Custom",
|
||||
Quantities=[complex_quantity, self.file.create_entity("IfcQuantityArea", Name="NetArea", AreaValue=5.0)],
|
||||
)
|
||||
self.file.create_entity(
|
||||
"IfcRelDefinesByProperties",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
RelatedObjects=[element],
|
||||
RelatingPropertyDefinition=quantity,
|
||||
)
|
||||
# A simple quantity in the same set still resolves normally.
|
||||
assert subject.get_element_value(element, "Qto_Custom.NetArea") == 5.0
|
||||
# Nested quantities of a complex quantity are reachable with the natural path.
|
||||
assert subject.get_element_value(element, "Qto_Custom.Layer1.Width") == 0.1
|
||||
assert subject.get_element_value(element, "Qto_Custom.Layer1.Height") == 2.5
|
||||
# The explicit "properties" path is preserved for backwards compatibility.
|
||||
assert subject.get_element_value(element, "Qto_Custom.Layer1.properties.Width") == 0.1
|
||||
|
||||
|
||||
class TestFilterElements(test.bootstrap.IFC4):
|
||||
def test_selecting_by_globalid(self):
|
||||
|
||||
Reference in New Issue
Block a user