diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py index dc4d2f6c35..91d48b3a95 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py @@ -39,7 +39,7 @@ class Usecase: if value is None: prop.NominalValue = None else: - primary_measure_type = self.get_primary_measure_type(prop.Name, previous_value=prop.NominalValue) + primary_measure_type = self.get_primary_measure_type(prop.Name, old_value=prop.NominalValue, new_value=value) prop.NominalValue = self.file.create_entity(primary_measure_type, value) del self.settings["properties"][prop.Name] @@ -48,7 +48,9 @@ class Usecase: for name, value in self.settings["properties"].items(): if value is None: continue - primary_measure_type = self.get_primary_measure_type(name) + primary_measure_type = self.get_primary_measure_type(name, new_value=value) + if hasattr(value, "is_a"): + value = value.wrappedValue properties.append( self.file.create_entity( "IfcPropertySingleValue", @@ -71,11 +73,22 @@ class Usecase: elif hasattr(self.settings["pset"], "Properties"): # For IfcMaterialProperties return self.settings["pset"].Properties or [] - def get_primary_measure_type(self, name, previous_value=None): - if not self.pset_template: - return previous_value.is_a() if previous_value else "IfcLabel" - for prop_template in self.pset_template.HasPropertyTemplates: - if prop_template.Name != name: - continue - return prop_template.PrimaryMeasureType or "IfcLabel" - return previous_value.is_a() if previous_value else "IfcLabel" + def get_primary_measure_type(self, name, old_value=None, new_value=None): + if self.pset_template: + for prop_template in self.pset_template.HasPropertyTemplates: + if prop_template.Name != name: + continue + return prop_template.PrimaryMeasureType or "IfcLabel" + if old_value: + return old_value.is_a() + elif new_value and hasattr(new_value, "is_a"): + return new_value.is_a() + elif new_value is not None: + if isinstance(new_value, str): + return "IfcLabel" + elif isinstance(new_value, float): + return "IfcReal" + elif isinstance(new_value, bool): + return "IfcBoolean" + elif isinstance(new_value, int): + return "IfcInteger" diff --git a/src/ifcopenshell-python/test/api/pset/__init__.py b/src/ifcopenshell-python/test/api/pset/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/ifcopenshell-python/test/api/pset/test_edit_pset.py b/src/ifcopenshell-python/test/api/pset/test_edit_pset.py new file mode 100644 index 0000000000..d00d5db5aa --- /dev/null +++ b/src/ifcopenshell-python/test/api/pset/test_edit_pset.py @@ -0,0 +1,172 @@ +import test.bootstrap +import ifcopenshell.api + + +class TestEditPset(test.bootstrap.IFC4): + def test_editing_a_blank_buildingsmart_templated_pset(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Pset_WallCommon") + ifcopenshell.api.run( + "pset.edit_pset", + self.file, + pset=pset, + properties={"Reference": "reference", "Status": "NEW", "Combustible": True, "ThermalTransmittance": 42}, + ) + pset = element.IsDefinedBy[0].RelatingPropertyDefinition + + assert pset.HasProperties[0].Name == "Reference" + assert pset.HasProperties[0].NominalValue.is_a("IfcIdentifier") + assert pset.HasProperties[0].NominalValue.wrappedValue == "reference" + + assert pset.HasProperties[1].Name == "Status" + assert pset.HasProperties[1].NominalValue.is_a("IfcLabel") + assert pset.HasProperties[1].NominalValue.wrappedValue == "NEW" + + assert pset.HasProperties[2].Name == "Combustible" + assert pset.HasProperties[2].NominalValue.is_a("IfcBoolean") + assert pset.HasProperties[2].NominalValue.wrappedValue == True + + assert pset.HasProperties[3].Name == "ThermalTransmittance" + assert pset.HasProperties[3].NominalValue.is_a("IfcThermalTransmittanceMeasure") + assert pset.HasProperties[3].NominalValue.wrappedValue == 42 + + def test_editing_an_existing_buildingsmart_templated_pset(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Pset_WallCommon") + ifcopenshell.api.run( + "pset.edit_pset", + self.file, + pset=pset, + properties={"Reference": "foo", "Status": "NEW", "Combustible": True, "ThermalTransmittance": 42}, + ) + ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Reference": "bar", "Status": None}) + pset = element.IsDefinedBy[0].RelatingPropertyDefinition + + assert pset.HasProperties[0].Name == "Reference" + assert pset.HasProperties[0].NominalValue.is_a("IfcIdentifier") + assert pset.HasProperties[0].NominalValue.wrappedValue == "bar" + + assert pset.HasProperties[1].Name == "Status" + assert pset.HasProperties[1].NominalValue is None + + def test_not_adding_a_property_if_it_is_none(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Pset_WallCommon") + ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Reference": None}) + pset = element.IsDefinedBy[0].RelatingPropertyDefinition + assert len(pset.HasProperties) == 0 + + def test_editing_a_pset_name(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="foo") + ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, name="bar") + pset = element.IsDefinedBy[0].RelatingPropertyDefinition + assert pset.Name == "bar" + + def test_adding_properties_without_a_template_with_autodetected_and_manual_data_types(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar") + ifcopenshell.api.run( + "pset.edit_pset", + self.file, + pset=pset, + properties={ + "MyLabel": "foobar", + "MyBool": True, + "MyInteger": 42, + "MyFloat": 42.0, + "MyCustom": self.file.createIfcContextDependentMeasure(123), + }, + ) + pset = element.IsDefinedBy[0].RelatingPropertyDefinition + + assert pset.HasProperties[0].Name == "MyLabel" + assert pset.HasProperties[0].NominalValue.is_a("IfcLabel") + assert pset.HasProperties[0].NominalValue.wrappedValue == "foobar" + + assert pset.HasProperties[1].Name == "MyBool" + assert pset.HasProperties[1].NominalValue.is_a("IfcBoolean") + assert pset.HasProperties[1].NominalValue.wrappedValue == True + + assert pset.HasProperties[2].Name == "MyInteger" + assert pset.HasProperties[2].NominalValue.is_a("IfcInteger") + assert pset.HasProperties[2].NominalValue.wrappedValue == 42 + + assert pset.HasProperties[3].Name == "MyFloat" + assert pset.HasProperties[3].NominalValue.is_a("IfcReal") + assert pset.HasProperties[3].NominalValue.wrappedValue == 42.0 + + assert pset.HasProperties[4].Name == "MyCustom" + assert pset.HasProperties[4].NominalValue.is_a("IfcContextDependentMeasure") + assert pset.HasProperties[4].NominalValue.wrappedValue == 123.0 + + def test_editing_properties_with_autodetected_existing_types(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar") + ifcopenshell.api.run( + "pset.edit_pset", + self.file, + pset=pset, + properties={ + "MyCustom": self.file.createIfcContextDependentMeasure(12), + }, + ) + ifcopenshell.api.run( + "pset.edit_pset", + self.file, + pset=pset, + properties={ + "MyCustom": 34, + }, + ) + pset = element.IsDefinedBy[0].RelatingPropertyDefinition + assert pset.HasProperties[0].Name == "MyCustom" + assert pset.HasProperties[0].NominalValue.is_a("IfcContextDependentMeasure") + assert pset.HasProperties[0].NominalValue.wrappedValue == 34 + + def test_editing_properties_of_non_rooted_elements(self): + element = self.file.createIfcMaterial() + pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar") + ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"foo": "bar"}) + + assert element.HasProperties[0] == pset + assert pset.Properties[0].Name == "foo" + assert pset.Properties[0].NominalValue.is_a("IfcLabel") + assert pset.Properties[0].NominalValue.wrappedValue == "bar" + + def test_editing_a_custom_templated_pset(self): + template = self.file.create_entity( + "IfcPropertySetTemplate", + **{ + "GlobalId": ifcopenshell.guid.new(), + "Name": "Foo_Bar", + "TemplateType": "PSET_TYPEDRIVENOVERRIDE", + "ApplicableEntity": "IfcWall", + "HasPropertyTemplates": [ + self.file.create_entity( + "IfcSimplePropertyTemplate", + **{ + "GlobalId": ifcopenshell.guid.new(), + "Name": "foo", + "TemplateType": "P_SINGLEVALUE", + "PrimaryMeasureType": "IfcContextDependentMeasure", + } + ) + ], + } + ) + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar") + ifcopenshell.api.run( + "pset.edit_pset", + self.file, + pset=pset, + pset_template=template, + properties={ + "foo": 12, + }, + ) + pset = element.IsDefinedBy[0].RelatingPropertyDefinition + assert pset.HasProperties[0].Name == "foo" + assert pset.HasProperties[0].NominalValue.is_a("IfcContextDependentMeasure") + assert pset.HasProperties[0].NominalValue.wrappedValue == 12