mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
Update IfcTester to comply with latest modifications to IDS XSD.
This commit is contained in:
@@ -343,9 +343,9 @@ class Classification(Facet):
|
||||
class PartOf(Facet):
|
||||
def __init__(
|
||||
self,
|
||||
entity=None,
|
||||
entity="IFCWALL",
|
||||
predefinedType=None,
|
||||
relation="IfcRelAggregates",
|
||||
relation=None,
|
||||
minOccurs=None,
|
||||
maxOccurs=None,
|
||||
instructions=None,
|
||||
@@ -390,7 +390,23 @@ class PartOf(Facet):
|
||||
return PartOfResult(True)
|
||||
|
||||
reason = None
|
||||
if self.relation == "IfcRelAggregates":
|
||||
if not self.relation:
|
||||
is_pass = False
|
||||
ancestors = []
|
||||
parent = self.get_parent(inst)
|
||||
while parent:
|
||||
ancestors.append(parent.is_a())
|
||||
if parent.is_a().upper() == self.entity:
|
||||
if self.predefinedType:
|
||||
if ifcopenshell.util.element.get_predefined_type(parent) == self.predefinedType:
|
||||
is_pass = True
|
||||
else:
|
||||
is_pass = True
|
||||
break
|
||||
parent = self.get_parent(parent)
|
||||
if not is_pass:
|
||||
reason = {"type": "ENTITY", "actual": ancestors}
|
||||
elif self.relation == "IFCRELAGGREGATES":
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(inst)
|
||||
is_pass = aggregate is not None
|
||||
if not is_pass:
|
||||
@@ -410,7 +426,7 @@ class PartOf(Facet):
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(aggregate)
|
||||
if not is_pass:
|
||||
reason = {"type": "ENTITY", "actual": ancestors}
|
||||
elif self.relation == "IfcRelAssignsToGroup":
|
||||
elif self.relation == "IFCRELASSIGNSTOGROUP":
|
||||
group = None
|
||||
for rel in getattr(inst, "HasAssignments", []) or []:
|
||||
if rel.is_a("IfcRelAssignsToGroup"):
|
||||
@@ -428,7 +444,7 @@ class PartOf(Facet):
|
||||
if predefined_type != self.predefinedType:
|
||||
is_pass = False
|
||||
reason = {"type": "PREDEFINEDTYPE", "actual": predefined_type}
|
||||
elif self.relation == "IfcRelContainedInSpatialStructure":
|
||||
elif self.relation == "IFCRELCONTAINEDINSPATIALSTRUCTURE":
|
||||
container = ifcopenshell.util.element.get_container(inst)
|
||||
is_pass = container is not None
|
||||
if not is_pass:
|
||||
@@ -442,7 +458,7 @@ class PartOf(Facet):
|
||||
if predefined_type != self.predefinedType:
|
||||
is_pass = False
|
||||
reason = {"type": "PREDEFINEDTYPE", "actual": predefined_type}
|
||||
elif self.relation == "IfcRelNests":
|
||||
elif self.relation == "IFCRELNESTS":
|
||||
nest = self.get_nested_whole(inst)
|
||||
is_pass = nest is not None
|
||||
if not is_pass:
|
||||
@@ -462,6 +478,36 @@ class PartOf(Facet):
|
||||
nest = self.get_nested_whole(nest)
|
||||
if not is_pass:
|
||||
reason = {"type": "ENTITY", "actual": ancestors}
|
||||
elif self.relation == "IFCRELVOIDSELEMENT":
|
||||
building_element = self.get_voided_element(inst)
|
||||
is_pass = building_element is not None
|
||||
if not is_pass:
|
||||
reason = {"type": "NOVALUE"}
|
||||
if is_pass and self.entity:
|
||||
is_pass = False
|
||||
if building_element.is_a().upper() == self.entity:
|
||||
if self.predefinedType:
|
||||
if ifcopenshell.util.element.get_predefined_type(building_element) == self.predefinedType:
|
||||
is_pass = True
|
||||
else:
|
||||
is_pass = True
|
||||
if not is_pass:
|
||||
reason = {"type": "ENTITY", "actual": building_element}
|
||||
elif self.relation == "IFCRELFILLSELEMENT":
|
||||
opening = self.filled_opening(inst)
|
||||
is_pass = opening is not None
|
||||
if not is_pass:
|
||||
reason = {"type": "NOVALUE"}
|
||||
if is_pass and self.entity:
|
||||
is_pass = False
|
||||
if opening.is_a().upper() == self.entity:
|
||||
if self.predefinedType:
|
||||
if ifcopenshell.util.element.get_predefined_type(opening) == self.predefinedType:
|
||||
is_pass = True
|
||||
else:
|
||||
is_pass = True
|
||||
if not is_pass:
|
||||
reason = {"type": "ENTITY", "actual": opening}
|
||||
|
||||
if self.maxOccurs == 0:
|
||||
return PartOfResult(not is_pass, {"type": "PROHIBITED"})
|
||||
@@ -471,6 +517,31 @@ class PartOf(Facet):
|
||||
for rel in getattr(element, "Nests", []) or []:
|
||||
return rel.RelatingObject
|
||||
|
||||
def get_voided_element(self, element):
|
||||
for rel in getattr(element, "VoidsElements", []) or []:
|
||||
return rel.RelatingBuildingElement
|
||||
|
||||
def get_filled_opening(self, element):
|
||||
for rel in getattr(element, "FillsVoids", []) or []:
|
||||
return rel.RelatingOpeningElement
|
||||
|
||||
def get_parent(self, element):
|
||||
parent = ifcopenshell.util.element.get_aggregate(element)
|
||||
if not parent:
|
||||
parent = ifcopenshell.util.element.get_container(element, should_get_direct=True)
|
||||
if not parent:
|
||||
for rel in getattr(element, "HasAssignments", []) or []:
|
||||
if rel.is_a("IfcRelAssignsToGroup"):
|
||||
parent = rel.RelatingGroup
|
||||
break
|
||||
if not parent:
|
||||
self.get_nested_whole(element)
|
||||
if not parent:
|
||||
self.get_voided_element(element)
|
||||
if not parent:
|
||||
self.get_filled_opening(element)
|
||||
return parent
|
||||
|
||||
|
||||
class Property(Facet):
|
||||
def __init__(
|
||||
@@ -478,7 +549,7 @@ class Property(Facet):
|
||||
propertySet="Property_Set",
|
||||
name="PropertyName",
|
||||
value=None,
|
||||
measure=None,
|
||||
datatype=None,
|
||||
uri=None,
|
||||
minOccurs=None,
|
||||
maxOccurs=None,
|
||||
@@ -488,7 +559,7 @@ class Property(Facet):
|
||||
"propertySet",
|
||||
"name",
|
||||
"value",
|
||||
"@measure",
|
||||
"@datatype",
|
||||
"@uri",
|
||||
"@minOccurs",
|
||||
"@maxOccurs",
|
||||
@@ -502,7 +573,7 @@ class Property(Facet):
|
||||
"{name} data shall be {value} and in the dataset {propertySet}",
|
||||
"{name} data shall be provided in the dataset {propertySet}",
|
||||
]
|
||||
super().__init__(propertySet, name, value, measure, uri, minOccurs, maxOccurs, instructions)
|
||||
super().__init__(propertySet, name, value, datatype, uri, minOccurs, maxOccurs, instructions)
|
||||
|
||||
def filter(self, ifc_file, elements):
|
||||
if isinstance(elements, list):
|
||||
@@ -566,9 +637,9 @@ class Property(Facet):
|
||||
elif prop_entity.is_a("IfcPropertySingleValue"):
|
||||
data_type = prop_entity.NominalValue.is_a()
|
||||
|
||||
if data_type != self.measure:
|
||||
if data_type != self.datatype:
|
||||
is_pass = False
|
||||
reason = {"type": "MEASURE", "actual": data_type}
|
||||
reason = {"type": "DATATYPE", "actual": data_type}
|
||||
break
|
||||
|
||||
unit = ifcopenshell.util.unit.get_property_unit(prop_entity, inst.wrapped_data.file)
|
||||
@@ -585,9 +656,9 @@ class Property(Facet):
|
||||
prop_schema = prop_entity.wrapped_data.declaration().as_entity()
|
||||
data_type = prop_schema.attribute_by_index(3).type_of_attribute().declared_type().name()
|
||||
|
||||
if data_type != self.measure:
|
||||
if data_type != self.datatype:
|
||||
is_pass = False
|
||||
reason = {"type": "MEASURE", "actual": data_type}
|
||||
reason = {"type": "DATATYPE", "actual": data_type}
|
||||
break
|
||||
|
||||
unit = ifcopenshell.util.unit.get_property_unit(prop_entity, inst.wrapped_data.file)
|
||||
@@ -605,9 +676,9 @@ class Property(Facet):
|
||||
reason = {"type": "NOVALUE"}
|
||||
break
|
||||
data_type = prop_entity.EnumerationValues[0].is_a()
|
||||
if data_type != self.measure:
|
||||
if data_type != self.datatype:
|
||||
is_pass = False
|
||||
reason = {"type": "MEASURE", "actual": data_type}
|
||||
reason = {"type": "DATATYPE", "actual": data_type}
|
||||
break
|
||||
elif prop_entity.is_a("IfcPropertyListValue"):
|
||||
if not prop_entity.ListValues:
|
||||
@@ -615,9 +686,9 @@ class Property(Facet):
|
||||
reason = {"type": "NOVALUE"}
|
||||
break
|
||||
data_type = prop_entity.ListValues[0].is_a()
|
||||
if data_type != self.measure:
|
||||
if data_type != self.datatype:
|
||||
is_pass = False
|
||||
reason = {"type": "MEASURE", "actual": data_type}
|
||||
reason = {"type": "DATATYPE", "actual": data_type}
|
||||
break
|
||||
unit = ifcopenshell.util.unit.get_property_unit(prop_entity, inst.wrapped_data.file)
|
||||
if unit:
|
||||
@@ -638,9 +709,9 @@ class Property(Facet):
|
||||
if value is not None:
|
||||
data_type = value.is_a()
|
||||
values.append(value.wrappedValue)
|
||||
if data_type != self.measure:
|
||||
if data_type != self.datatype:
|
||||
is_pass = False
|
||||
reason = {"type": "MEASURE", "actual": data_type}
|
||||
reason = {"type": "DATATYPE", "actual": data_type}
|
||||
break
|
||||
unit = ifcopenshell.util.unit.get_property_unit(prop_entity, inst.wrapped_data.file)
|
||||
if unit:
|
||||
@@ -663,7 +734,7 @@ class Property(Facet):
|
||||
if not column_values:
|
||||
continue
|
||||
data_type = column_values[0].is_a()
|
||||
if data_type == self.measure:
|
||||
if data_type == self.datatype:
|
||||
column_values = [v.wrappedValue for v in column_values]
|
||||
unit = units[f"{attribute}Unit"]
|
||||
if unit:
|
||||
@@ -680,7 +751,7 @@ class Property(Facet):
|
||||
values.extend(column_values)
|
||||
if not values:
|
||||
is_pass = False
|
||||
reason = {"type": "MEASURE", "actual": data_type}
|
||||
reason = {"type": "DATATYPE", "actual": data_type}
|
||||
break
|
||||
props[pset_name][prop_entity.Name] = values
|
||||
else:
|
||||
@@ -963,7 +1034,7 @@ class PropertyResult(Result):
|
||||
return "The required property set does not exist"
|
||||
elif self.reason["type"] == "NOVALUE":
|
||||
return "The property set does not contain the required property"
|
||||
elif self.reason["type"] == "MEASURE":
|
||||
elif self.reason["type"] == "DATATYPE":
|
||||
return f"The data type \"{str(self.reason['actual'])}\" does not match the requirements"
|
||||
elif self.reason["type"] == "VALUE":
|
||||
if isinstance(self.reason["actual"], list):
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<!-- edited with XMLSpy v2022 (x64) (http://www.altova.com) -->
|
||||
<!-- September 26, 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.3">
|
||||
<!-- June 20, 2023 - 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" xmlns:altova="http://www.altova.com/xml-schema-extensions" targetNamespace="http://standards.buildingsmart.org/IDS" elementFormDefault="qualified" attributeFormDefault="unqualified" version="0.9.6">
|
||||
<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" schemaLocation="http://www.w3.org/2001/XMLSchema.xsd"/>
|
||||
<xs:import namespace="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="http://www.w3.org/2001/XMLSchema-instance"/>
|
||||
<xs:element name="ids">
|
||||
<xs:complexType>
|
||||
@@ -27,13 +26,7 @@
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="specifications">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="ids:specificationsType"/>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="specifications" type="ids:specificationsType"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
@@ -58,9 +51,9 @@
|
||||
</xs:complexType>
|
||||
<xs:complexType name="partOfType">
|
||||
<xs:sequence>
|
||||
<xs:element name="entity" type="ids:entityType" minOccurs="0"/>
|
||||
<xs:element name="entity" type="ids:entityType" minOccurs="1" />
|
||||
</xs:sequence>
|
||||
<xs:attribute name="relation" type="ids:relations" use="required"/>
|
||||
<xs:attribute name="relation" type="ids:relations" />
|
||||
</xs:complexType>
|
||||
<xs:complexType name="applicabilityType">
|
||||
<xs:sequence>
|
||||
@@ -68,20 +61,8 @@
|
||||
<xs:element name="partOf" type="ids:partOfType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="classification" type="ids:classificationType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="attribute" type="ids:attributeType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="property" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="ids:propertyType"/>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="material" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="ids:materialType"/>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="property" type="ids:propertyType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="material" type="ids:materialType" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="propertyType">
|
||||
@@ -90,11 +71,11 @@
|
||||
<xs:element name="name" type="ids:idsValue"/>
|
||||
<xs:element name="value" type="ids:idsValue" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="measure" type="xs:string">
|
||||
<xs:attribute name="datatype" use="required">
|
||||
<xs:annotation>
|
||||
<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/units.md</xs:documentation>
|
||||
<xs:documentation>This is the name of an IFC Defined Type. See the full list for IFC 4 on https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/link/alphabeticalorder-defined-types.htm Documentation and default units on https://github.com/buildingSMART/IDS/blob/master/Documentation/units.md</xs:documentation>
|
||||
</xs:annotation>
|
||||
<!-- there is a request to make this an ids:value -->
|
||||
<!-- renamed 'measure' to data type to better represent reality -->
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="attributeType">
|
||||
@@ -116,7 +97,13 @@
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="ids:entityType"/>
|
||||
<xs:extension base="ids:entityType">
|
||||
<xs:attribute name="instructions" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Author of the IDS can leave instructions for the authors of the IFC. This text could/should be displayed in the BIM/IFC authoring tool.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
@@ -153,7 +140,6 @@
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="ids:attributeType">
|
||||
<xs:attributeGroup ref="xs:occurs"/>
|
||||
<xs:attribute name="instructions" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Author of the IDS can leave instructions for the authors of the IFC. This text could/should be displayed in the BIM/IFC authoring tool.</xs:documentation>
|
||||
@@ -215,6 +201,7 @@
|
||||
<xs:list>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:minLength value="1"/>
|
||||
<xs:enumeration value="IFC2X3"/>
|
||||
<xs:enumeration value="IFC4"/>
|
||||
<xs:enumeration value="IFC4X3"/>
|
||||
@@ -225,7 +212,7 @@
|
||||
</xs:attribute>
|
||||
<xs:attribute name="identifier" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Author of the IDS can provide an identifier to the IDS. Beware: this cannot be enforced/assumed as (global) unique.</xs:documentation>
|
||||
<xs:documentation>Author of the IDS can provide an identifier to the specification. This is intended to be a machine readable identifier. Beware: because of the possibility to combine different 'requirement' elements from several ids files this cannot be enforced/assumed as (global) unique.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="description" type="xs:string" use="optional"/>
|
||||
@@ -242,10 +229,12 @@
|
||||
</xs:complexType>
|
||||
<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:enumeration value="IFCRELAGGREGATES"/>
|
||||
<xs:enumeration value="IFCRELASSIGNSTOGROUP"/>
|
||||
<xs:enumeration value="IFCRELCONTAINEDINSPATIALSTRUCTURE"/>
|
||||
<xs:enumeration value="IFCRELNESTS"/>
|
||||
<xs:enumeration value="IFCRELVOIDSELEMENT"/>
|
||||
<xs:enumeration value="IFCRELFILLSELEMENT"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
||||
</xs:schema>
|
||||
Reference in New Issue
Block a user