mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 10:51:13 +00:00
Fix #2420. Bug where project library assets sharing relationships with other assets would pull in other assets
This commit is contained in:
@@ -29,17 +29,22 @@ class Usecase:
|
|||||||
self.settings[key] = value
|
self.settings[key] = value
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
self.added_elements = set()
|
self.added_elements = {}
|
||||||
self.whitelisted_inverse_attributes = {}
|
self.whitelisted_inverse_attributes = {}
|
||||||
if self.settings["element"].is_a("IfcTypeProduct"):
|
if self.settings["element"].is_a("IfcTypeProduct"):
|
||||||
|
self.target_class = "IfcTypeProduct"
|
||||||
return self.append_type_product()
|
return self.append_type_product()
|
||||||
elif self.settings["element"].is_a("IfcProduct"):
|
elif self.settings["element"].is_a("IfcProduct"):
|
||||||
|
self.target_class = "IfcProduct"
|
||||||
return self.append_product()
|
return self.append_product()
|
||||||
elif self.settings["element"].is_a("IfcMaterial"):
|
elif self.settings["element"].is_a("IfcMaterial"):
|
||||||
|
self.target_class = "IfcMaterial"
|
||||||
return self.append_material()
|
return self.append_material()
|
||||||
elif self.settings["element"].is_a("IfcCostSchedule"):
|
elif self.settings["element"].is_a("IfcCostSchedule"):
|
||||||
|
self.target_class = "IfcCostSchedule"
|
||||||
return self.append_cost_schedule()
|
return self.append_cost_schedule()
|
||||||
elif self.settings["element"].is_a("IfcProfileDef"):
|
elif self.settings["element"].is_a("IfcProfileDef"):
|
||||||
|
self.target_class = "IfcProfileDef"
|
||||||
return self.append_profile_def()
|
return self.append_profile_def()
|
||||||
|
|
||||||
def get_existing_element(self):
|
def get_existing_element(self):
|
||||||
@@ -147,16 +152,18 @@ class Usecase:
|
|||||||
return element
|
return element
|
||||||
|
|
||||||
def add_element(self, element):
|
def add_element(self, element):
|
||||||
if element.id() == 0 or element.id() in self.added_elements:
|
if element.id() == 0:
|
||||||
return
|
return
|
||||||
|
if element.id() in self.added_elements:
|
||||||
|
return self.added_elements[element.id()]
|
||||||
new = self.file.add(element)
|
new = self.file.add(element)
|
||||||
self.added_elements.add(element.id())
|
self.added_elements[element.id()] = new
|
||||||
self.add_inverse(element)
|
self.check_inverses(element)
|
||||||
for subelement in self.settings["library"].traverse(element):
|
for subelement in self.settings["library"].traverse(element):
|
||||||
self.add_inverse(subelement)
|
self.check_inverses(subelement)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def add_inverse(self, element):
|
def check_inverses(self, element):
|
||||||
for source_class, attributes in self.whitelisted_inverse_attributes.items():
|
for source_class, attributes in self.whitelisted_inverse_attributes.items():
|
||||||
if not element.is_a(source_class):
|
if not element.is_a(source_class):
|
||||||
continue
|
continue
|
||||||
@@ -166,9 +173,39 @@ class Usecase:
|
|||||||
attribute, attribute_class = attribute.split(".")
|
attribute, attribute_class = attribute.split(".")
|
||||||
for inverse in getattr(element, attribute, []):
|
for inverse in getattr(element, attribute, []):
|
||||||
if attribute_class and inverse.is_a(attribute_class):
|
if attribute_class and inverse.is_a(attribute_class):
|
||||||
self.add_element(inverse)
|
self.add_inverse_element(inverse)
|
||||||
elif not attribute_class:
|
elif not attribute_class:
|
||||||
self.add_element(inverse)
|
self.add_inverse_element(inverse)
|
||||||
|
|
||||||
|
def add_inverse_element(self, element):
|
||||||
|
# Inverse attributes are added manually because they are basically
|
||||||
|
# relationships that can reference many other assets that we are not
|
||||||
|
# interested in.
|
||||||
|
new = self.file.create_entity(element.is_a())
|
||||||
|
for i, attribute in enumerate(element):
|
||||||
|
new_attribute = None
|
||||||
|
if isinstance(attribute, ifcopenshell.entity_instance):
|
||||||
|
if not self.is_another_asset(attribute):
|
||||||
|
new_attribute = self.add_element(attribute)
|
||||||
|
elif isinstance(attribute, tuple) and attribute and isinstance(attribute[0], ifcopenshell.entity_instance):
|
||||||
|
new_attribute = []
|
||||||
|
for item in attribute:
|
||||||
|
if not self.is_another_asset(item):
|
||||||
|
new_attribute.append(self.add_element(item))
|
||||||
|
else:
|
||||||
|
new_attribute = attribute
|
||||||
|
if new_attribute is not None:
|
||||||
|
new[i] = new_attribute
|
||||||
|
|
||||||
|
def is_another_asset(self, element):
|
||||||
|
if element == self.settings["element"]:
|
||||||
|
return False
|
||||||
|
elif element.is_a("IfcFeatureElement"):
|
||||||
|
# Feature elements match the target class but aren't considered "assets"
|
||||||
|
return False
|
||||||
|
elif element.is_a(self.target_class):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def get_equivalent_existing_context(self, added_context):
|
def get_equivalent_existing_context(self, added_context):
|
||||||
for context in self.existing_contexts:
|
for context in self.existing_contexts:
|
||||||
|
|||||||
@@ -44,6 +44,16 @@ class TestAppendAsset(test.bootstrap.IFC4):
|
|||||||
ifcopenshell.api.run("project.append_asset", self.file, library=library, element=element)
|
ifcopenshell.api.run("project.append_asset", self.file, library=library, element=element)
|
||||||
assert len(self.file.by_type("IfcWallType")) == 1
|
assert len(self.file.by_type("IfcWallType")) == 1
|
||||||
|
|
||||||
|
def test_append_a_single_type_product_even_though_an_inverse_material_relationship_is_shared(self):
|
||||||
|
library = ifcopenshell.api.run("project.create_file")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType")
|
||||||
|
element2 = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType")
|
||||||
|
material = ifcopenshell.api.run("material.add_material", library, name="Material")
|
||||||
|
ifcopenshell.api.run("material.assign_material", library, product=element, material=material)
|
||||||
|
ifcopenshell.api.run("material.assign_material", library, product=element2, material=material)
|
||||||
|
ifcopenshell.api.run("project.append_asset", self.file, library=library, element=element)
|
||||||
|
assert len(self.file.by_type("IfcWallType")) == 1
|
||||||
|
|
||||||
def test_append_a_type_product_with_its_materials(self):
|
def test_append_a_type_product_with_its_materials(self):
|
||||||
library = ifcopenshell.api.run("project.create_file")
|
library = ifcopenshell.api.run("project.create_file")
|
||||||
element = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType")
|
element = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType")
|
||||||
|
|||||||
Reference in New Issue
Block a user