diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py index 195d09e614..57e8fe5c4a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py @@ -1,5 +1,5 @@ # IfcOpenShell - IFC toolkit and geometry engine -# Copyright (C) 2021 Dion Moult +# Copyright (C) 2021, 2022 Dion Moult # # This file is part of IfcOpenShell. # @@ -31,6 +31,13 @@ class Usecase: self.settings[key] = value def execute(self): + if isinstance(self.settings["classification"], str): + classification = self.file.createIfcClassification(Name=self.settings["classification"]) + self.relate_to_project(classification) + return classification + return self.add_from_library() + + def add_from_library(self): edition_date = None if self.settings["classification"].EditionDate: edition_date = ifcopenshell.util.date.ifc2datetime(self.settings["classification"].EditionDate) @@ -47,17 +54,20 @@ class Usecase: else: result.EditionDate = ifcopenshell.util.date.datetime2ifc(edition_date, "IfcDate") - self.file.create_entity( - "IfcRelAssociatesClassification", - **{ - "GlobalId": ifcopenshell.guid.new(), - "RelatedObjects": [self.file.by_type("IfcProject")[0]], - "RelatingClassification": result, - } - ) + self.relate_to_project(result) + return result # See bug #1272 + try: result = self.file.add(self.settings["classification"]) except: migrator = ifcopenshell.util.schema.Migrator() result = migrator.migrate(self.settings["classification"], self.file) + + def relate_to_project(self, classification): + self.file.create_entity( + "IfcRelAssociatesClassification", + GlobalId=ifcopenshell.guid.new(), + RelatedObjects=[self.file.by_type("IfcProject")[0]], + RelatingClassification=classification, + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py index 0d271ec135..44083b94c3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py @@ -26,6 +26,8 @@ class Usecase: self.settings = { "product": None, "reference": None, + "identification": None, + "name": None, "classification": None, "is_lightweight": True, } @@ -33,29 +35,34 @@ class Usecase: self.settings[key] = value def execute(self): - relating_classification = None + if self.settings["reference"]: + return self.add_from_library() + return self.add_from_identification() + def add_from_identification(self): + reference = self.get_existing_reference(self.settings["identification"]) + if reference: + self.add_to_existing_relationship() + else: + reference = self.file.createIfcClassificationReference( + Identification=self.settings["identification"], + Name=self.settings["name"], + ReferencedSource=self.settings["classification"], + ) + self.add_new_relationship(reference) + return reference + + def add_from_library(self): if hasattr(self.settings["reference"], "ItemReference"): identification = self.settings["reference"].ItemReference # IFC2X3 else: identification = self.settings["reference"].Identification - for reference in self.file.by_type("IfcClassificationReference"): - if self.file.schema == "IFC2X3": - if reference.ItemReference == identification: - relating_classification = reference - break - else: - if reference.Identification == identification: - relating_classification = reference - break + reference = self.get_existing_reference(identification) - if relating_classification: - association = self.get_association(relating_classification) - related_objects = set(association.RelatedObjects) - related_objects.add(self.settings["product"]) - association.RelatedObjects = list(related_objects) - return + if reference: + self.add_to_existing_relationship() + return reference migrator = ifcopenshell.util.schema.Migrator() @@ -67,31 +74,46 @@ class Usecase: c for c in self.file.by_type("IfcClassification") if c.Name == self.settings["classification"].Name ] - relating_classification = migrator.migrate(self.settings["reference"], self.file) + reference = migrator.migrate(self.settings["reference"], self.file) if self.settings["is_lightweight"]: - relating_classification.ReferencedSource = self.settings["classification"] + reference.ReferencedSource = self.settings["classification"] self.settings["reference"].ReferencedSource = old_referenced_source elif existing_classification: to_delete = set() - for traversed_reference in self.file.traverse(relating_classification): + for traversed_reference in self.file.traverse(reference): if traversed_reference.ReferencedSource.is_a("IfcClassification"): to_delete.add(traversed_reference.ReferencedSource) traversed_reference.ReferencedSource = existing_classification[0] break for element in to_delete: self.file.remove(element) + self.add_new_relationship(reference) + def get_existing_reference(self, identification): + for reference in self.file.by_type("IfcClassificationReference"): + if self.file.schema == "IFC2X3": + if reference.ItemReference == identification: + return reference + else: + if reference.Identification == identification: + return reference + + def add_new_relationship(self, reference): self.file.create_entity( "IfcRelAssociatesClassification", - **{ - "GlobalId": ifcopenshell.guid.new(), - "RelatedObjects": [self.settings["product"]], - "RelatingClassification": relating_classification, - } + GlobalId=ifcopenshell.guid.new(), + RelatedObjects=[self.settings["product"]], + RelatingClassification=reference, ) - def get_association(self, reference): + def add_to_existing_relationship(self, reference): + rel = self.get_rel_associates_classification(reference) + related_objects = set(rel.RelatedObjects) + related_objects.add(self.settings["product"]) + rel.RelatedObjects = list(related_objects) + + def get_rel_associates_classification(self, reference): if self.file.schema == "IFC2X3": for association in self.file.by_type("IfcRelAssociatesClassification"): if association.RelatingClassification == reference: diff --git a/src/ifcopenshell-python/test/api/classification/test_add_classification.py b/src/ifcopenshell-python/test/api/classification/test_add_classification.py new file mode 100644 index 0000000000..2c80cfb52f --- /dev/null +++ b/src/ifcopenshell-python/test/api/classification/test_add_classification.py @@ -0,0 +1,34 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api + + +class TestAddClassification(test.bootstrap.IFC4): + def test_adding_a_classification(self): + ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject") + ifcopenshell.api.run("classification.add_classification", self.file, classification="Name") + assert self.file.by_type("IfcClassification")[0].Name == "Name" + + def test_adding_a_classification_from_a_library(self): + library = ifcopenshell.file() + classification = library.createIfcClassification(Name="Name") + ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject") + ifcopenshell.api.run("classification.add_classification", self.file, classification=classification) + assert self.file.by_type("IfcClassification")[0].Name == "Name" diff --git a/src/ifcopenshell-python/test/api/classification/test_add_classification_reference.py b/src/ifcopenshell-python/test/api/classification/test_add_classification_reference.py new file mode 100644 index 0000000000..50a4d9ec66 --- /dev/null +++ b/src/ifcopenshell-python/test/api/classification/test_add_classification_reference.py @@ -0,0 +1,61 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api +import ifcopenshell.util.classification + + +class TestAddReference(test.bootstrap.IFC4): + def test_adding_a_reference(self): + ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject") + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name") + ifcopenshell.api.run( + "classification.add_reference", + self.file, + product=element, + identification="X", + name="Foobar", + classification=result, + ) + references = list(ifcopenshell.util.classification.get_references(element)) + assert len(references) == 1 + assert references[0].Identification == "X" + assert references[0].Name == "Foobar" + assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0] + + def test_adding_a_reference_from_a_library(self): + library = ifcopenshell.file() + classification = library.createIfcClassification(Name="Name") + reference = library.createIfcClassificationReference(Identification="1", ReferencedSource=classification) + + ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject") + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + result = ifcopenshell.api.run("classification.add_classification", self.file, classification=classification) + ifcopenshell.api.run( + "classification.add_reference", + self.file, + product=element, + reference=reference, + classification=result, + ) + references = list(ifcopenshell.util.classification.get_references(element)) + assert len(references) == 1 + assert references[0].Identification == "1" + assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]