mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
Implement type casting for property checks in IDS, bump to IDS v0.9
This commit is contained in:
@@ -28,8 +28,9 @@ def cast_to_value(from_value, to_value):
|
||||
try:
|
||||
target_type = type(to_value).__name__
|
||||
if target_type == "int":
|
||||
# Casting str -> float -> int means that notation like '1e3' is preserved
|
||||
return int(float(from_value))
|
||||
# Casting str -> float means that notation like '1e3' is preserved
|
||||
# We do not cast to int because 42.0 == 42 and 42.3 != 42
|
||||
return float(from_value)
|
||||
elif target_type == "bool":
|
||||
if from_value == "TRUE":
|
||||
return True
|
||||
@@ -366,7 +367,11 @@ class Property(Facet):
|
||||
props[pset_name] = {}
|
||||
if isinstance(self.name, str):
|
||||
prop = pset_props.get(self.name)
|
||||
if prop is not None and prop != "":
|
||||
if prop == "UNKNOWN" and [
|
||||
p for p in inst.wrapped_data.file.by_id(pset_props["id"]).HasProperties if p.Name == self.name
|
||||
][0].NominalValue.is_a("IfcLogical"):
|
||||
pass
|
||||
elif prop is not None and prop != "":
|
||||
props[pset_name][self.name] = prop
|
||||
else:
|
||||
props[pset_name] = {k: v for k, v in pset_props.items() if k == self.name}
|
||||
@@ -386,7 +391,7 @@ class Property(Facet):
|
||||
):
|
||||
continue
|
||||
|
||||
data_type = prop_entity.NominalValue.is_a().replace("Ifc", "").replace("Measure", "")
|
||||
data_type = prop_entity.NominalValue.is_a()
|
||||
|
||||
if data_type != self.measure:
|
||||
is_pass = False
|
||||
@@ -407,10 +412,22 @@ class Property(Facet):
|
||||
break
|
||||
|
||||
if self.value:
|
||||
if any([v != self.value for v in props[pset_name].values()]):
|
||||
is_pass = False
|
||||
reason = {"type": "VALUE", "actual": list(props[pset_name].values())}
|
||||
break
|
||||
for value in props[pset_name].values():
|
||||
if isinstance(self.value, str) and isinstance(value, str):
|
||||
if value != self.value:
|
||||
is_pass = False
|
||||
reason = {"type": "VALUE", "actual": value}
|
||||
break
|
||||
elif isinstance(self.value, str):
|
||||
cast_value = cast_to_value(self.value, value)
|
||||
if value != cast_value:
|
||||
is_pass = False
|
||||
reason = {"type": "VALUE", "actual": value}
|
||||
break
|
||||
elif value != self.value:
|
||||
is_pass = False
|
||||
reason = {"type": "VALUE", "actual": value}
|
||||
break
|
||||
return PropertyResult(is_pass, reason)
|
||||
|
||||
|
||||
@@ -654,7 +671,7 @@ class PropertyResult(Result):
|
||||
return "The property set does not contain the required property"
|
||||
elif self.reason["type"] == "MEASURE":
|
||||
return f"The data type \"{str(self.reason['actual'])}\" does not match the requirements"
|
||||
elif self.reason["type"] == "VALUE" and len(self.reason['actual']) == 1:
|
||||
elif self.reason["type"] == "VALUE" and len(self.reason["actual"]) == 1:
|
||||
return f"The property value \"{str(self.reason['actual'][0])}\" does not match the requirements"
|
||||
elif self.reason["type"] == "VALUE":
|
||||
return f"The property values \"{str(self.reason['actual'])}\" do not match the requirements"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- edited with XMLSpy v2022 (x64) (http://www.altova.com) by Leon van Berlo (Overleaf Investments B.V.) -->
|
||||
<!-- May 24, 2022 - DRAFT -->
|
||||
<xs:schema xmlns:ids="http://standards.buildingsmart.org/IDS" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="http://standards.buildingsmart.org/IDS" elementFormDefault="qualified" attributeFormDefault="unqualified" version="0.6.1">
|
||||
<!-- August 2, 2022 - DRAFT -->
|
||||
<xs:schema xmlns:ids="http://standards.buildingsmart.org/IDS" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="http://standards.buildingsmart.org/IDS" elementFormDefault="qualified" attributeFormDefault="unqualified" version="0.9.0">
|
||||
<xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>
|
||||
<xs:import namespace="http://www.w3.org/2001/XMLSchema" schemaLocation="https://www.w3.org/2001/XMLSchema.xsd"/>
|
||||
<xs:import namespace="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="http://www.w3.org/2001/XMLSchema-instance"/>
|
||||
@@ -83,67 +83,11 @@
|
||||
<xs:element name="name" type="ids:idsValue"/>
|
||||
<xs:element name="value" type="ids:idsValue" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="measure">
|
||||
<xs:attribute name="measure" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>See the documentation and default units of these measures on https://github.com/buildingSMART/IDS/blob/master/Documentation/Physical_Quantities_and_Units.md</xs:documentation>
|
||||
<xs:documentation>This is the name of an IFC Defined Types. See the documentation and default units on https://github.com/buildingSMART/IDS/blob/master/Documentation/Physical_Quantities_and_Units.md</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="String"/>
|
||||
<xs:enumeration value="Number"/>
|
||||
<xs:enumeration value="AmountOfSubstance"/>
|
||||
<xs:enumeration value="AreaDensity"/>
|
||||
<xs:enumeration value="Area"/>
|
||||
<xs:enumeration value="DynamicViscosity"/>
|
||||
<xs:enumeration value="ElectricCapacitance"/>
|
||||
<xs:enumeration value="ElectricCharge"/>
|
||||
<xs:enumeration value="ElectricConductance"/>
|
||||
<xs:enumeration value="ElectricCurrent"/>
|
||||
<xs:enumeration value="ElectricResistance"/>
|
||||
<xs:enumeration value="ElectricVoltage"/>
|
||||
<xs:enumeration value="Energy"/>
|
||||
<xs:enumeration value="Force"/>
|
||||
<xs:enumeration value="Frequency"/>
|
||||
<xs:enumeration value="HeatFluxDensity"/>
|
||||
<xs:enumeration value="Heating"/>
|
||||
<xs:enumeration value="Illuminance"/>
|
||||
<xs:enumeration value="IonConcentration"/>
|
||||
<xs:enumeration value="IsoThermalMoistureCapacity"/>
|
||||
<xs:enumeration value="Length"/>
|
||||
<xs:enumeration value="Speed"/>
|
||||
<xs:enumeration value="LuminousFlux"/>
|
||||
<xs:enumeration value="LuminousIntensity"/>
|
||||
<xs:enumeration value="MassDensity"/>
|
||||
<xs:enumeration value="MassFlowRate"/>
|
||||
<xs:enumeration value="Mass"/>
|
||||
<xs:enumeration value="MassPerLength"/>
|
||||
<xs:enumeration value="ModulusOfElasticity"/>
|
||||
<xs:enumeration value="MoistureDiffusivity"/>
|
||||
<xs:enumeration value="MolecularWeight"/>
|
||||
<xs:enumeration value="MomentOfInertia"/>
|
||||
<xs:enumeration value="PH"/>
|
||||
<xs:enumeration value="PlanarForce"/>
|
||||
<xs:enumeration value="Angle"/>
|
||||
<xs:enumeration value="PlaneAngle"/>
|
||||
<xs:enumeration value="Power"/>
|
||||
<xs:enumeration value="Pressure"/>
|
||||
<xs:enumeration value="RadioActivity"/>
|
||||
<xs:enumeration value="Ratio"/>
|
||||
<xs:enumeration value="RotationalFrequency"/>
|
||||
<xs:enumeration value="SectionModulus"/>
|
||||
<xs:enumeration value="SoundPower"/>
|
||||
<xs:enumeration value="SoundPressure"/>
|
||||
<xs:enumeration value="SpecificHeatCapacity"/>
|
||||
<xs:enumeration value="TemperatureRateOfChange"/>
|
||||
<xs:enumeration value="ThermalConductivity"/>
|
||||
<xs:enumeration value="Temperature"/>
|
||||
<xs:enumeration value="Time"/>
|
||||
<xs:enumeration value="Torque"/>
|
||||
<xs:enumeration value="VaporPermeability"/>
|
||||
<xs:enumeration value="Volume"/>
|
||||
<xs:enumeration value="VolumetricFlowRate"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<!-- there is a request to make this an ids:value -->
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="attributeType">
|
||||
@@ -169,41 +113,19 @@
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="partOf" minOccurs="0" maxOccurs="unbounded">
|
||||
<!-- Proposed definition
|
||||
<xs:sequence>
|
||||
<xs:element name="entity" type="ids:idsValue"/>
|
||||
</xs:sequence>
|
||||
<xs:element name="partOf" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="relationship" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="IfcRelAggregates"/>
|
||||
<xs:enumeration value="IfcRelAssignsToGroup"/>
|
||||
<xs:enumeration value="IfcRelContainedInSpatialStructure"/>
|
||||
<xs:enumeration value="IfcRelNests"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
-->
|
||||
<!-- Current definition -->
|
||||
<xs:complexType>
|
||||
<xs:attribute name="entity" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="IfcElementAssembly"/>
|
||||
<xs:enumeration value="IfcGroup"/>
|
||||
<xs:enumeration value="IfcSystem"/>
|
||||
<xs:enumeration value="IfcBuildingSystem"/>
|
||||
<xs:enumeration value="IfcBuiltSystem"/>
|
||||
<xs:enumeration value="IfcDistributionSystem"/>
|
||||
<xs:enumeration value="IfcZone"/>
|
||||
<xs:enumeration value="IfcAsset"/>
|
||||
<xs:enumeration value="IfcInventory"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:sequence>
|
||||
<xs:element name="entity" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="ids:idsValue">
|
||||
<xs:attribute name="relation" type="ids:relations" use="required"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="classification" minOccurs="0" maxOccurs="unbounded">
|
||||
@@ -270,7 +192,15 @@
|
||||
<xs:complexType name="specificationType">
|
||||
<xs:sequence>
|
||||
<xs:element name="applicability" type="ids:applicabilityType"/>
|
||||
<xs:element name="requirements" type="ids:requirementsType"/>
|
||||
<xs:element name="requirements">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="ids:requirementsType">
|
||||
<xs:attribute name="description" type="xs:string" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:string" use="required"/>
|
||||
<xs:attributeGroup ref="xs:occurs"/>
|
||||
@@ -304,4 +234,12 @@
|
||||
<xs:element name="specification" type="ids:specificationType" minOccurs="1" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
||||
<xs:simpleType name="relations">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="IfcRelAggregates"/>
|
||||
<xs:enumeration value="IfcRelAssignsToGroup"/>
|
||||
<xs:enumeration value="IfcRelContainedInSpatialStructure"/>
|
||||
<xs:enumeration value="IfcRelNests"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
||||
@@ -401,8 +401,6 @@ class TestAttribute:
|
||||
expected=False,
|
||||
)
|
||||
|
||||
# TODO continue from here
|
||||
|
||||
global_id = ifcopenshell.guid.new()
|
||||
facet = Attribute(name="GlobalId", value=global_id)
|
||||
ifc = ifcopenshell.file()
|
||||
@@ -509,27 +507,10 @@ class TestAttribute:
|
||||
facet = Attribute(name="NumberOfRisers", value="42.3")
|
||||
ifc = ifcopenshell.file()
|
||||
run(
|
||||
"Integers are always floored when cast 1/2",
|
||||
"Specifying a float when the value is an integer will fail",
|
||||
facet=facet,
|
||||
inst=ifc.createIfcStairFlight(NumberOfRisers=42),
|
||||
expected=True,
|
||||
)
|
||||
facet = Attribute(name="NumberOfRisers", value="42.7")
|
||||
ifc = ifcopenshell.file()
|
||||
run(
|
||||
"Integers are always floored when cast 2/2",
|
||||
facet=facet,
|
||||
inst=ifc.createIfcStairFlight(NumberOfRisers=42),
|
||||
expected=True,
|
||||
)
|
||||
|
||||
facet = Attribute(name="NumberOfRisers", value="42.7")
|
||||
ifc = ifcopenshell.file()
|
||||
run(
|
||||
"Integers are always floored when cast 2/2",
|
||||
facet=facet,
|
||||
inst=ifc.createIfcStairFlight(NumberOfRisers=42),
|
||||
expected=True,
|
||||
expected=False,
|
||||
)
|
||||
|
||||
facet = Attribute(name="IsMilestone", value="TRUE")
|
||||
@@ -841,134 +822,177 @@ class TestProperty:
|
||||
timeunit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="TIMEUNIT", name="SECOND")
|
||||
ifcopenshell.api.run("unit.assign_unit", ifc, units=[lengthunit, areaunit, volumeunit, timeunit])
|
||||
|
||||
# A name check by itself only checks that a property is non-null and non empty string
|
||||
# The logic is that unfortunately most BIM users cannot differentiate between the two.
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo")
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
run("Elements with no properties always fail", facet=facet, inst=element, expected=False)
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"AnotherProperty": "AnotherValue"})
|
||||
run("Elements with a matching pset but no property also fail", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": None})
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
run("Properties with a null value fail", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run("A name check will match any property with any string value", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ""})
|
||||
run("An empty string is considered falsey and will not pass", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcLogical("UNKNOWN")})
|
||||
run("A logical unknown is considered falsey and will not pass", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcDuration("P0D")})
|
||||
run("A zero duration will pass", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcBoolean(True)})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run("A property set to true will pass a name check", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": False})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run(
|
||||
"A property set to false is still considered a value and will pass a name check",
|
||||
facet=facet,
|
||||
inst=element,
|
||||
expected=True,
|
||||
)
|
||||
|
||||
# A simple value checks an exact case-sensitive match
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="Bar")
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run("Specifying a value performs a case-sensitive match 1/2", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "bar"})
|
||||
run("Specifying a value performs a case-sensitive match 2/2", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Baz"})
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
run("Specifying a value fails against different values", facet=facet, inst=element, expected=False)
|
||||
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="♫")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "♫"})
|
||||
run("Non-ascii characters are treated without encoding", facet=facet, inst=element, expected=True)
|
||||
|
||||
identifier = "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value=identifier + "_extra_characters")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcIdentifier(identifier)})
|
||||
run("IDS does not handle string truncation such as for identifiers", facet=facet, inst=element, expected=False)
|
||||
|
||||
# Simple values only check string matches
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="1")
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "1"})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcInteger(1)})
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
run("A number specified as a string is treated as a string", facet=facet, inst=element, expected=True)
|
||||
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="42")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcInteger(42)})
|
||||
run("Integer values are checked using type casting 1/4", facet=facet, inst=element, expected=True)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="42.")
|
||||
run("Integer values are checked using type casting 2/4", facet=facet, inst=element, expected=True)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="42.0")
|
||||
run("Integer values are checked using type casting 3/4", facet=facet, inst=element, expected=True)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="42.3")
|
||||
run("Integer values are checked using type casting 4/4", facet=facet, inst=element, expected=False)
|
||||
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="42")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.0)})
|
||||
run("Real values are checked using type casting 1/3", facet=facet, inst=element, expected=True)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="42.0")
|
||||
run("Real values are checked using type casting 2/3", facet=facet, inst=element, expected=True)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="42.3")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.3)})
|
||||
run("Real values are checked using type casting 3/3", facet=facet, inst=element, expected=True)
|
||||
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="42,3")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.3)})
|
||||
run("Only specifically formatted numbers are allowed 1/4", facet=facet, inst=element, expected=False)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="123,4.5")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcReal(1234.5)})
|
||||
run("Only specifically formatted numbers are allowed 2/4", facet=facet, inst=element, expected=False)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="1.2345e3")
|
||||
run("Only specifically formatted numbers are allowed 3/4", facet=facet, inst=element, expected=True)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="1.2345E3")
|
||||
run("Only specifically formatted numbers are allowed 4/4", facet=facet, inst=element, expected=True)
|
||||
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="TRUE")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcBoolean(False)})
|
||||
run("Booleans must be specified as uppercase strings 1/3", facet=facet, inst=element, expected=False)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="FALSE")
|
||||
run("Booleans must be specified as uppercase strings 2/3", facet=facet, inst=element, expected=True)
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="False")
|
||||
run("Booleans must be specified as uppercase strings 3/3", facet=facet, inst=element, expected=False)
|
||||
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="2022-01-01")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcDate("2022-01-01")})
|
||||
run("Dates are treated as strings 1/2", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcDate("2022-01-01+00:00")}
|
||||
)
|
||||
run("Dates are treated as strings 2/2", facet=facet, inst=element, expected=False)
|
||||
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="PT16H")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcDuration("PT16H")})
|
||||
run("Durations are treated as strings 1/2", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcDuration("P2D")})
|
||||
run("Durations are treated as strings 1/2", facet=facet, inst=element, expected=False)
|
||||
|
||||
# Restrictions are supported for property sets. If multiple are matched, all must satisfy requirements.
|
||||
restriction = Restriction(options="Foo_.*", type="pattern")
|
||||
facet = Property(propertySet=restriction, name="Foo")
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run("All matching property sets must satisfy requirements 1/3", facet=facet, inst=element, expected=True)
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Baz")
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"AnotherProperty": "AnotherValue"})
|
||||
run("All matching property sets must satisfy requirements 2/3", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run("All matching property sets must satisfy requirements 3/3", facet=facet, inst=element, expected=True)
|
||||
|
||||
# Restrictions are supported for names. If multiple are matched, all must satisfy requirements.
|
||||
restriction = Restriction(options="Foo.*", type="pattern")
|
||||
facet = Property(propertySet="Foo_Bar", name=restriction, value="x")
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x"})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run("All matching properties must satisfy requirements 1/3", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "x"})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run("All matching properties must satisfy requirements 2/3", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "y"})
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
run("All matching properties must satisfy requirements 3/3", facet=facet, inst=element, expected=False)
|
||||
|
||||
# Restrictions are supported for values. If multiple are matched, all must satisfy requirements.
|
||||
restriction1 = Restriction(options="Foo.*", type="pattern")
|
||||
restriction2 = Restriction(options=["x", "y"], type="enumeration")
|
||||
facet = Property(propertySet="Foo_Bar", name=restriction1, value=restriction2)
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "y"})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run(
|
||||
"If multiple properties are matched, all values must satisfy requirements 1/2",
|
||||
facet=facet,
|
||||
inst=element,
|
||||
expected=True,
|
||||
)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "z"})
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
run(
|
||||
"If multiple properties are matched, all values must satisfy requirements 2/2",
|
||||
facet=facet,
|
||||
inst=element,
|
||||
expected=False,
|
||||
)
|
||||
|
||||
# Restrictions may be used to check basic data primitives
|
||||
restriction = Restriction(options=[42.12], type="enumeration", base="decimal")
|
||||
facet = Property(propertySet="Foo_Bar", name="Foobar", value=restriction)
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": 42.12})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
restriction = Restriction(options=[42], type="enumeration", base="integer")
|
||||
facet = Property(propertySet="Foo_Bar", name="Foobar", value=restriction)
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": 42})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
restriction = Restriction(options=[True], type="enumeration", base="boolean")
|
||||
facet = Property(propertySet="Foo_Bar", name="Foobar", value=restriction)
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": True})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": False})
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
|
||||
# When measure is not specified, no unit conversion is done and only primitives are checked
|
||||
restriction = Restriction(options=[42.12], type="enumeration", base="decimal")
|
||||
facet = Property(propertySet="Foo_Bar", name="Foobar", value=restriction)
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": 42.12})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
|
||||
# Measure may be used to specify an IFC data type
|
||||
restriction = Restriction(options=[2], type="enumeration", base="decimal")
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value=restriction, measure="Time")
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="2", measure="IfcTimeMeasure")
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcMassMeasure(2)})
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
run("Measure may be used to specify an IFC data type 1/2", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcTimeMeasure(2)})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run("Measure may be used to specify an IFC data type 2/2", facet=facet, inst=element, expected=True)
|
||||
|
||||
# Measure also implies that a unit matters, and so a conversion shall take place to SI units
|
||||
restriction = Restriction(options=[2], type="enumeration", base="decimal")
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value=restriction, measure="Length")
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="2", measure="IfcLengthMeasure")
|
||||
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcLengthMeasure(2)})
|
||||
run("", facet=facet, inst=element, expected=False)
|
||||
run("Unit conversions shall take place to IDS-nominated standard units 1/2", facet=facet, inst=element, expected=False)
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcLengthMeasure(2000)})
|
||||
run("", facet=facet, inst=element, expected=True)
|
||||
run("Unit conversions shall take place to IDS-nominated standard units 2/2", facet=facet, inst=element, expected=True)
|
||||
|
||||
# The facet checks inherited properties from the type
|
||||
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||
ifcopenshell.api.run("type.assign_type", ifc, related_object=wall, relating_type=wall_type)
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=wall_type, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo")
|
||||
run("", facet=facet, inst=wall, expected=True)
|
||||
run("", facet=facet, inst=wall_type, expected=True)
|
||||
run("Properties can be inherited from the type 1/2", facet=facet, inst=wall, expected=True)
|
||||
run("Properties can be inherited from the type 2/2", facet=facet, inst=wall_type, expected=True)
|
||||
|
||||
# The facet checks overriden properties from the occurrence
|
||||
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||
ifcopenshell.api.run("type.assign_type", ifc, related_object=wall, relating_type=wall_type)
|
||||
@@ -977,8 +1001,8 @@ class TestProperty:
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=wall, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||
facet = Property(propertySet="Foo_Bar", name="Foo", value="Bar")
|
||||
run("", facet=facet, inst=wall, expected=True)
|
||||
run("", facet=facet, inst=wall_type, expected=False)
|
||||
run("Properties can be overriden by an occurrence 1/2", facet=facet, inst=wall, expected=True)
|
||||
run("Properties can be overriden by an occurrence 2/2", facet=facet, inst=wall_type, expected=False)
|
||||
|
||||
|
||||
class TestMaterial:
|
||||
|
||||
Reference in New Issue
Block a user