diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index 7f02dc609b..ed121ac95d 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -19,7 +19,7 @@ import ifcopenshell -def get_pset(element, name, prop=None, should_inherit=True): +def get_pset(element, name, prop=None, should_inherit=True, verbose=False): """Retrieve a single property set or single property This is more efficient than ifcopenshell.util.element.get_psets if you know @@ -64,7 +64,7 @@ def get_pset(element, name, prop=None, should_inherit=True): if should_inherit: element_type = ifcopenshell.util.element.get_type(element) if element_type: - type_pset = get_pset(element_type, name, prop, should_inherit=False) + type_pset = get_pset(element_type, name, prop, should_inherit=False, verbose=verbose) for relationship in element.IsDefinedBy: if relationship.is_a("IfcRelDefinesByProperties"): definition = relationship.RelatingPropertyDefinition @@ -77,19 +77,19 @@ def get_pset(element, name, prop=None, should_inherit=True): if not prop: if type_pset: - occurrence_pset = get_property_definition(pset) + occurrence_pset = get_property_definition(pset, verbose=verbose) if occurrence_pset: type_pset.update(occurrence_pset) return type_pset - return get_property_definition(pset) + return get_property_definition(pset, verbose=verbose) - value = get_property_definition(pset, prop) + value = get_property_definition(pset, prop=prop, verbose=verbose) if value is None and type_pset is not None: return type_pset return value -def get_psets(element, psets_only=False, qtos_only=False, should_inherit=True): +def get_psets(element, psets_only=False, qtos_only=False, should_inherit=True, verbose=False): """Retrieve property sets, their related properties' names & values and ids. If should_inherit is true, the pset "id" only refers to the ID of the @@ -122,12 +122,12 @@ def get_psets(element, psets_only=False, qtos_only=False, should_inherit=True): continue if qtos_only and not definition.is_a("IfcElementQuantity"): continue - psets[definition.Name] = get_property_definition(definition) + psets[definition.Name] = get_property_definition(definition, verbose=verbose) elif element.is_a("IfcMaterialDefinition") or element.is_a("IfcProfileDef"): for definition in getattr(element, "HasProperties", None) or []: if qtos_only: continue - psets[definition.Name] = get_property_definition(definition) + psets[definition.Name] = get_property_definition(definition, verbose=verbose) elif hasattr(element, "IsDefinedBy"): if should_inherit: element_type = ifcopenshell.util.element.get_type(element) @@ -140,21 +140,21 @@ def get_psets(element, psets_only=False, qtos_only=False, should_inherit=True): continue if qtos_only and not definition.is_a("IfcElementQuantity"): continue - psets.setdefault(definition.Name, {}).update(get_property_definition(definition)) + psets.setdefault(definition.Name, {}).update(get_property_definition(definition, verbose=verbose)) return psets -def get_property_definition(definition, prop=None): +def get_property_definition(definition, prop=None, verbose=False): if not definition: return if prop: if definition.is_a("IfcElementQuantity"): - return get_quantity(definition.Quantities, prop) + return get_quantity(definition.Quantities, prop, verbose=verbose) elif definition.is_a("IfcPropertySet"): - return get_property(definition.HasProperties, prop) + return get_property(definition.HasProperties, prop, verbose=verbose) elif definition.is_a("IfcMaterialProperties") or definition.is_a("IfcProfileProperties"): - return get_property(definition.Properties, prop) + return get_property(definition.Properties, prop, verbose=verbose) else: # Entity introduced in IFC4 # definition.is_a('IfcPreDefinedPropertySet'): @@ -166,11 +166,11 @@ def get_property_definition(definition, prop=None): props = {} if definition.is_a("IfcElementQuantity"): - props.update(get_quantities(definition.Quantities)) + props.update(get_quantities(definition.Quantities, verbose=verbose)) elif definition.is_a("IfcPropertySet"): - props.update(get_properties(definition.HasProperties)) + props.update(get_properties(definition.HasProperties, verbose=verbose)) elif definition.is_a("IfcMaterialProperties") or definition.is_a("IfcProfileProperties"): - props.update(get_properties(definition.Properties)) + props.update(get_properties(definition.Properties, verbose=verbose)) else: # Entity introduced in IFC4 # definition.is_a('IfcPreDefinedPropertySet'): @@ -181,76 +181,105 @@ def get_property_definition(definition, prop=None): return props -def get_quantity(quantities, name): +def get_quantity(quantities, name, verbose=False): for quantity in quantities or []: if quantity.Name != name: continue if quantity.is_a("IfcPhysicalSimpleQuantity"): - return quantity[3] - results[quantity.Name] = quantity[3] + result = quantity[3] elif quantity.is_a("IfcPhysicalComplexQuantity"): data = {k: v for k, v in quantity.get_info().items() if v is not None and k != "Name"} data["properties"] = get_quantities(quantity.HasQuantities) del data["HasQuantities"] - return data + result = data + if verbose: + result = {"id": quantity.id(), "class": quantity.is_a(), "value": result} + return result -def get_quantities(quantities, name=None): +def get_quantities(quantities, verbose=False): results = {} for quantity in quantities or []: if quantity.is_a("IfcPhysicalSimpleQuantity"): results[quantity.Name] = quantity[3] + if verbose: + results[quantity.Name] = { + "id": quantity.id(), + "class": quantity.is_a(), + "value": results[quantity.Name], + } elif quantity.is_a("IfcPhysicalComplexQuantity"): data = {k: v for k, v in quantity.get_info().items() if v is not None and k != "Name"} data["properties"] = get_quantities(quantity.HasQuantities) del data["HasQuantities"] results[quantity.Name] = data + if verbose: + results[quantity.Name] = { + "id": quantity.id(), + "class": quantity.is_a(), + "value": results[quantity.Name], + } return results -def get_property(properties, name): +def get_property(properties, name, verbose=False): for prop in properties or []: if prop.Name != name: continue if prop.is_a("IfcPropertySingleValue"): - return prop.NominalValue.wrappedValue if prop.NominalValue else None + result = prop.NominalValue.wrappedValue if prop.NominalValue else None elif prop.is_a("IfcPropertyEnumeratedValue"): - return [v.wrappedValue for v in prop.EnumerationValues] if prop.EnumerationValues else None + result = [v.wrappedValue for v in prop.EnumerationValues] if prop.EnumerationValues else None elif prop.is_a("IfcPropertyListValue"): - return [v.wrappedValue for v in prop.ListValues] or None + result = [v.wrappedValue for v in prop.ListValues] or None elif prop.is_a("IfcPropertyBoundedValue"): data = prop.get_info() del data["Unit"] - return data + result = data elif prop.is_a("IfcPropertyTableValue"): - return prop.get_info() + result = prop.get_info() elif prop.is_a("IfcComplexProperty"): data = {k: v for k, v in prop.get_info().items() if v is not None and k != "Name"} data["properties"] = get_properties(prop.HasProperties) del data["HasProperties"] - return data + result = data + if verbose: + result = {"id": prop.id(), "class": prop.is_a(), "value": result} + return result -def get_properties(properties): +def get_properties(properties, verbose=False): results = {} for prop in properties or []: if prop.is_a("IfcPropertySingleValue"): results[prop.Name] = prop.NominalValue.wrappedValue if prop.NominalValue else None + if verbose: + results[prop.Name] = {"id": prop.id(), "class": prop.is_a(), "value": results[prop.Name]} elif prop.is_a("IfcPropertyEnumeratedValue"): results[prop.Name] = [v.wrappedValue for v in prop.EnumerationValues] if prop.EnumerationValues else None + if verbose: + results[prop.Name] = {"id": prop.id(), "class": prop.is_a(), "value": results[prop.Name]} elif prop.is_a("IfcPropertyListValue"): results[prop.Name] = [v.wrappedValue for v in prop.ListValues] or None + if verbose: + results[prop.Name] = {"id": prop.id(), "class": prop.is_a(), "value": results[prop.Name]} elif prop.is_a("IfcPropertyBoundedValue"): data = prop.get_info() del data["Unit"] results[prop.Name] = data + if verbose: + results[prop.Name] = {"id": prop.id(), "class": prop.is_a(), "value": results[prop.Name]} elif prop.is_a("IfcPropertyTableValue"): results[prop.Name] = prop.get_info() + if verbose: + results[prop.Name] = {"id": prop.id(), "class": prop.is_a(), "value": results[prop.Name]} elif prop.is_a("IfcComplexProperty"): data = {k: v for k, v in prop.get_info().items() if v is not None and k != "Name"} data["properties"] = get_properties(prop.HasProperties) del data["HasProperties"] results[prop.Name] = data + if verbose: + results[prop.Name] = {"id": prop.id(), "class": prop.is_a(), "value": results[prop.Name]} return results diff --git a/src/ifcopenshell-python/test/util/test_element.py b/src/ifcopenshell-python/test/util/test_element.py index 499f448567..9e6f5ffaad 100644 --- a/src/ifcopenshell-python/test/util/test_element.py +++ b/src/ifcopenshell-python/test/util/test_element.py @@ -31,6 +31,11 @@ class TestGetPsetIFC4(test.bootstrap.IFC4): ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"a": "b"}) assert subject.get_pset(element, "name") == {"a": "b", "id": pset.id()} assert subject.get_pset(element, "name", "a") == "b" + assert subject.get_pset(element, "name", "a", verbose=True) == { + "id": 4, + "class": "IfcPropertySingleValue", + "value": "b", + } def test_getting_the_psets_of_a_product_type_as_a_dictionary(self): type_element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") @@ -38,6 +43,14 @@ class TestGetPsetIFC4(test.bootstrap.IFC4): pset = ifcopenshell.api.run("pset.add_pset", self.file, product=type_element, name="name") ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"x": "y"}) assert subject.get_pset(type_element, "name") == {"x": "y", "id": pset.id()} + assert subject.get_pset(type_element, "name", verbose=True) == { + "x": { + "id": 3, + "class": "IfcPropertySingleValue", + "value": "y", + }, + "id": pset.id(), + } def test_getting_inherited_psets(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") @@ -401,34 +414,55 @@ class TestGetStyles(test.bootstrap.IFC4): assert subject.get_styles(element) == [] model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model") - body = ifcopenshell.api.run("context.add_context", self.file, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model) + body = ifcopenshell.api.run( + "context.add_context", + self.file, + context_type="Model", + context_identifier="Body", + target_view="MODEL_VIEW", + parent=model, + ) material = ifcopenshell.api.run("material.add_material", self.file) ifcopenshell.api.run("material.assign_material", self.file, product=element, material=material) style = ifcopenshell.api.run("style.add_style", self.file) - ifcopenshell.api.run("style.add_surface_style", self.file, - style=style, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) + ifcopenshell.api.run( + "style.add_surface_style", + self.file, + style=style, + ifc_class="IfcSurfaceStyleShading", + attributes={ + "SurfaceColour": {"Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8}, + "Transparency": 0.0, # 0 is opaque, 1 is transparent + }, + ) ifcopenshell.api.run("style.assign_material_style", self.file, material=material, style=style, context=body) assert subject.get_styles(element) == [style] style2 = ifcopenshell.api.run("style.add_style", self.file) - ifcopenshell.api.run("style.add_surface_style", self.file, - style=style2, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) + ifcopenshell.api.run( + "style.add_surface_style", + self.file, + style=style2, + ifc_class="IfcSurfaceStyleShading", + attributes={ + "SurfaceColour": {"Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8}, + "Transparency": 0.0, # 0 is opaque, 1 is transparent + }, + ) - representation = ifcopenshell.api.run("geometry.add_wall_representation", self.file, - context=body, length=5, height=3, thickness=0.118) + representation = ifcopenshell.api.run( + "geometry.add_wall_representation", self.file, context=body, length=5, height=3, thickness=0.118 + ) - ifcopenshell.api.run("geometry.assign_representation", self.file, product=element, representation=representation) - ifcopenshell.api.run("style.assign_representation_styles", self.file, shape_representation=representation, styles=[style2]) + ifcopenshell.api.run( + "geometry.assign_representation", self.file, product=element, representation=representation + ) + ifcopenshell.api.run( + "style.assign_representation_styles", self.file, shape_representation=representation, styles=[style2] + ) assert subject.get_styles(element) == [style, style2]