Compare commits

..

16 Commits

Author SHA1 Message Date
Dion Moult da1bdc802d Accommodate invalid models coming from Cadwork 2024-05-14 16:16:18 +10:00
Andrej730 a9cba30da6 ifc2x3 tests
removed part of test_append_two_type_products_sharing_the_same_material_indirectly_via_a_material_set for ifc2x3 compatibility and removed part is already tested in test_append_two_type_products_sharing_the_same_material_with_properties
2024-05-13 18:01:18 +05:00
Andrej730 3665dda87c library.remove_library, remove_reference to support ifc2x3 2024-05-13 18:01:18 +05:00
Andrej730 f2696d5352 avoid confusing TypeErrors from api calls
After ab5ea4c85 it was always throwing wrong singature errors like below even if TypeError was caused by some internal issues inside API - it was adding couple extra steps to traceback making errors more noisy.

TypeError: Incorrect function arguments provided for library.edit_library
attribute 'VersionDate' for entity 'IFC2X3.IfcLibraryInformation' is expecting value of type 'ENTITY INSTANCE', got 'str'.. You specified args (<ifcopenshell.file.file object at 0x0000027EB8E6BCD0>,) and settings {'library': #1=IfcLibraryInformation('Name','Version',$,$,$), 'attributes': {'Name': 'Name', 'Version': 'Version', 'VersionDate': 'VersionDate', 'Location': 'Location', 'Description': 'Description'}}
E
Correct signature is (file: ifcopenshell.file.file, library: ifcopenshell.entity_instance.entity_instance, attributes: dict[str, typing.Any]) -> None
See help(ifcopenshell.api.library.edit_library) for documentation.
2024-05-13 18:01:18 +05:00
Andrej730 5e394e1576 library.edit_library - support datetime for VersionDate attribute 2024-05-13 18:01:18 +05:00
Andrej730 ebd03e9290 typing 2024-05-13 18:01:17 +05:00
Andrej730 c3bfd7354f pset.add_pset - throw an error if entity doesn't support adding a pset 2024-05-13 18:01:17 +05:00
Andrej730 1d046eaadf material.remove_material and copy_material to handle ifc2x3 props 2024-05-13 18:01:17 +05:00
Andrej730 2f554db54b owner.remove_person to remove IfcInventory in ifc2x3 2024-05-13 18:01:17 +05:00
Andrej730 54b4cef25e fix errors appending related materials in ifc2x3
it wasn't processing materials properties correctly because it was expecting material to have class IfcMaterialDefinition
2024-05-13 18:01:17 +05:00
Andrej730 c6106e6636 fix edit_pset error for editing material properties in ifc2x3 2024-05-13 18:01:17 +05:00
Andrej730 865dbab3ec fix error removing array pset after c50d1ea2f 2024-05-13 18:01:17 +05:00
Andrej730 76e6c63a01 ifc2x3 tests - prevent using removed user/application
When some test was creating an element and then removing it, it would also remove user and application as they wasn't used anywhere else.
`ifcopenshell.util.element.remove_deep2(file, history)` we use in every api for element deletion can possibly remove user and application which can be unsafe if `get_user` is returning some specific entity that then will become invalid.

Also fixed tests breaking due ifcownerhistory and user/application appearing in ifc2x3.
2024-05-13 18:01:16 +05:00
Gorgious56 3320accc7a The error box is now drawn in red to draw attention to it 2024-05-13 12:00:01 +02:00
Gorgious56 ac686db298 Prevent error when user deletes all spatial structure elements and the spatial manager panel is expanded 2024-05-13 11:57:48 +02:00
Dion Moult aca26c7597 Fix #4659. 2024-05-12 17:16:50 +10:00
105 changed files with 823 additions and 516 deletions
+4 -3
View File
@@ -608,7 +608,8 @@ class IfcImporter:
threshold = 10000 # Just from experience.
faces = [len(e.CfsFaces) for e in self.file.by_type("IfcClosedShell")]
# The check for CfsFaces/Faces/CoordIndex accommodates invalid data from Cadwork
faces = [len(e.CfsFaces) for e in self.file.by_type("IfcClosedShell") if e.CfsFaces]
if faces and max(faces) > threshold:
self.ifc_import_settings.should_use_native_meshes = True
return
@@ -616,12 +617,12 @@ class IfcImporter:
if self.file.schema == "IFC2X3":
return
faces = [len(e.Faces) for e in self.file.by_type("IfcPolygonalFaceSet")]
faces = [len(e.Faces) for e in self.file.by_type("IfcPolygonalFaceSet") if e.Faces]
if faces and max(faces) > threshold:
self.ifc_import_settings.should_use_native_meshes = True
return
faces = [len(e.CoordIndex) for e in self.file.by_type("IfcTriangulatedFaceSet")]
faces = [len(e.CoordIndex) for e in self.file.by_type("IfcTriangulatedFaceSet") if e.CoordIndex]
if faces and max(faces) > threshold:
self.ifc_import_settings.should_use_native_meshes = True
@@ -83,17 +83,8 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
props = obj.BIMArrayProperties
relating_obj = props.relating_array_object
if relating_obj:
element = tool.Ifc.get_entity(relating_obj)
parent_globalid = ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Parent")
parent_element = tool.Ifc.get().by_guid(parent_globalid)
data = json.loads(ifcopenshell.util.element.get_pset(parent_element, "BBIM_Array", "Data"))[self.item]
else:
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
props.count = data["count"]
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
props.x = data["x"] * si_conversion
@@ -102,9 +93,7 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
props.use_local_space = data.get("use_local_space", False)
props.sync_children = data.get("sync_children", False)
props.method = data.get("method", "OFFSET")
props.is_editing = self.item
return {"FINISHED"}
@@ -148,10 +137,6 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
tool.Blender.Modifier.Array.set_children_lock_state(element, self.item, True)
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
#clears the relating_array_object so it doesn't show again next time
props.relating_array_object = None
return {"FINISHED"}
@@ -208,7 +193,7 @@ class RemoveArray(bpy.types.Operator, tool.Ifc.Operator):
pset = tool.Ifc.get().by_id(pset["id"])
if len(data) == 1:
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
else:
del data[self.item]
data = tool.Ifc.get().createIfcText(json.dumps(data))
@@ -87,21 +87,6 @@ def update_type_page(self, context):
AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types()
def update_relating_array_from_object(self, context):
bpy.ops.bim.enable_editing_array(item=self.is_editing)
return
def is_object_array_applicable(self, obj):
element = tool.Ifc.get_entity(obj)
if not element:
return False
return ifcopenshell.util.element.get_pset(element, "BBIM_Array")
class BIMModelProperties(PropertyGroup):
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class)
relating_type_id: bpy.props.EnumProperty(
@@ -218,14 +203,6 @@ class BIMArrayProperties(PropertyGroup):
description="Regenerate all children based on the parent object",
default=False,
)
relating_array_object: bpy.props.PointerProperty(
type=bpy.types.Object,
name="Copy Array Properties",
update=update_relating_array_from_object,
poll=is_object_array_applicable,
)
class BIMStairProperties(PropertyGroup):
@@ -223,8 +223,6 @@ class BIM_PT_array(bpy.types.Panel):
row = col.row(align=True)
row.prop(props, "z")
row.operator("bim.input_cursor_z_array", icon="CURSOR", text="")
row = col.row(align=True)
row.prop(props, "relating_array_object", icon="COPYDOWN")
else:
row = box.row(align=True)
name = f"{array['count']} Items ({array.get('method', 'OFFSET').capitalize()})"
@@ -44,6 +44,8 @@ def update_elevation(self, context):
def update_active_container_index(self, context):
if self.active_container_index < 0:
return
self.active_container_id = self.containers[self.active_container_index].ifc_definition_id
self.container_name = self.containers[self.active_container_index].name
self.elevation = self.containers[self.active_container_index].elevation
@@ -116,7 +116,7 @@ class BIM_PT_SpatialManager(Panel):
self.props = context.scene.BIMSpatialManagerProperties
row = self.layout.row()
row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="Load Spatial Structure")
if self.props.active_container_index < len(self.props.containers):
if 0 <= self.props.active_container_index < len(self.props.containers):
ifc_definition_id = self.props.containers[self.props.active_container_index].ifc_definition_id
row = self.layout.row()
row.alignment = "RIGHT"
@@ -134,7 +134,7 @@ class BIM_PT_SpatialManager(Panel):
"active_container_index",
)
row = self.layout.row()
if self.props.active_container_index < len(self.props.containers):
if 0 <= self.props.active_container_index < len(self.props.containers):
row.prop(self.props, "container_name", text="")
row.prop(self.props, "elevation", text="")
op = row.operator("bim.edit_container_attributes", icon="CHECKMARK", text="Apply")
+1
View File
@@ -406,6 +406,7 @@ class BIM_PT_tabs(Panel):
if blenderbim.last_error:
box = self.layout.box()
box.alert=True
row = box.row(align=True)
row.label(text="BlenderBIM experienced an error :(", icon="ERROR")
row.operator("bim.close_error", text="", icon="CANCEL")
+1 -1
View File
@@ -47,7 +47,7 @@ def add_representation(
data = geometry.get_object_data(obj)
if not data and ifc_representation_class != "IfcTextLiteral":
raise IncompatibleRepresentationError()
return
representation = ifc.run(
"geometry.add_representation",
@@ -324,8 +324,18 @@ def wrap_usecase(usecase_path, usecase):
try:
result = usecase(*args, **settings)
except TypeError as e:
msg = f"Incorrect function arguments provided for {usecase_path}\n{str(e)}. You specified args {args} and settings {settings}\n\nCorrect signature is {inspect.signature(usecase)}\nSee help(ifcopenshell.api.{usecase_path}) for documentation."
except NotImplementedError as e:
if not e.args[0].startswith(f"{usecase.__name__}()"):
# signature errors typically start with function name
# e.g. "TypeError: edit_library() got an unexpected keyword argument 'test'"
# otherwise it's an error inside api call and we shouldn't get in the way
raise e
msg = (
f"Incorrect function arguments provided for {usecase_path}\n{str(e)}. "
f"You specified args {args} and settings {settings}\n\n"
f"Correct signature is {inspect.signature(usecase)}\n"
f"See help(ifcopenshell.api.{usecase_path}) for documentation."
)
raise TypeError(msg) from e
if should_run_listeners:
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Optional
def add_layer(file, Name=None) -> None:
def add_layer(file: ifcopenshell.file, Name: Optional[str] = None) -> ifcopenshell.entity_instance:
"""Adds a new layer
An IFC layer is like a CAD layer. Portions of an object's geometry
@@ -41,6 +43,4 @@ def add_layer(file, Name=None) -> None:
ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL-FULL-DIMS-N")
"""
settings = {"Name": Name or "Unnamed"}
return file.create_entity("IfcPresentationLayerAssignment", Name=settings["Name"])
return file.create_entity("IfcPresentationLayerAssignment", Name=Name)
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_layer(file, layer=None, attributes=None) -> None:
def edit_layer(file: ifcopenshell.file, layer: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcPresentationLayerAssignment
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_layer(file, layer=None, attributes=None) -> None:
:param layer: The IfcPresentationLayerAssignment entity you want to edit
:type layer: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -38,7 +40,7 @@ def edit_layer(file, layer=None, attributes=None) -> None:
ifcopenshell.api.run("layer.edit_layer", model,
layer=layer, attributes={"Description": "All walls, based on the AIA standard."})
"""
settings = {"layer": layer, "attributes": attributes or {}}
settings = {"layer": layer, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["layer"], name, value)
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def remove_layer(file, layer=None) -> None:
def remove_layer(file: ifcopenshell.file, layer: ifcopenshell.entity_instance) -> None:
"""Removes a layer
All representation items assigned to the layer will remain, but the
@@ -35,6 +36,4 @@ def remove_layer(file, layer=None) -> None:
layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL")
ifcopenshell.api.run("layer.remove_layer", model, layer=layer)
"""
settings = {"layer": layer}
file.remove(settings["layer"])
file.remove(layer)
@@ -21,7 +21,7 @@ import ifcopenshell.util.schema
import ifcopenshell.util.date
def add_library(file, name=None) -> None:
def add_library(file: ifcopenshell.entity_instance, name: str) -> ifcopenshell.entity_instance:
"""Adds a new library to the project
A library is an external data source that is related to the project. It
@@ -60,6 +60,4 @@ def add_library(file, name=None) -> None:
ifcopenshell.api.run("library.add_library", model, name="Brickschema")
"""
settings = {"name": name}
return file.create_entity("IfcLibraryInformation", Name=settings["name"])
return file.create_entity("IfcLibraryInformation", Name=name)
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.date
import datetime
from typing import Any
def edit_library(file, library=None, attributes=None) -> None:
def edit_library(file: ifcopenshell.file, library: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcLibraryInformation
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_library(file, library=None, attributes=None) -> None:
:param library: The IfcLibraryInformation entity you want to edit
:type library: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -39,7 +43,16 @@ def edit_library(file, library=None, attributes=None) -> None:
attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."})
"""
settings = {"library": library, "attributes": attributes or {}}
if "VersionDate" in attributes:
dt = attributes["VersionDate"]
if isinstance(dt, datetime.datetime):
if file.schema != "IFC2X3":
dt = ifcopenshell.util.date.datetime2ifc(dt, "IfcDateTime")
else:
calendar_date = ifcopenshell.util.date.datetime2ifc(dt, "IfcCalendarDate")
dt = file.create_entity("IfcCalendarDate", **calendar_date)
attributes = attributes.copy()
attributes["VersionDate"] = dt
for name, value in settings["attributes"].items():
setattr(settings["library"], name, value)
for name, value in attributes.items():
setattr(library, name, value)
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_reference(file, reference=None, attributes=None) -> None:
def edit_reference(
file: ifcopenshell.file, reference: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcLibraryReference
For more information about the attributes and data types of an
@@ -26,7 +30,7 @@ def edit_reference(file, reference=None, attributes=None) -> None:
:param reference: The IfcLibraryReference entity you want to edit
:type reference: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -40,7 +44,7 @@ def edit_reference(file, reference=None, attributes=None) -> None:
ifcopenshell.api.run("library.edit_reference", model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
"""
settings = {"reference": reference, "attributes": attributes or {}}
settings = {"reference": reference, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["reference"], name, value)
@@ -20,7 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_library(file, library=None) -> None:
def remove_library(file: ifcopenshell.file, library: ifcopenshell.entity_instance) -> None:
"""Removes a library
All references along with their relationships will also be removed. Any
@@ -38,14 +38,23 @@ def remove_library(file, library=None) -> None:
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
ifcopenshell.api.run("library.remove_library", model, library=library)
"""
settings = {"library": library}
for reference in set(settings["library"].HasLibraryReferences or []):
file.remove(reference)
file.remove(settings["library"])
for rel in file.by_type("IfcRelAssociatesLibrary"):
if not rel.RelatingLibrary:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
if file.schema != "IFC2X3":
rels = []
for reference in set(library.HasLibraryReferences):
rels.extend(reference.LibraryRefForObjects)
file.remove(reference)
rels.extend(library.LibraryInfoForObjects)
file.remove(library)
else:
for reference in set(library.LibraryReference or []):
file.remove(reference)
file.remove(library)
# RelatingLibrary could either be library itself or library reference we removed
rels = [rel for rel in file.by_type("IfcRelAssociatesLibrary") if rel.RelatingLibrary is None]
for rel in rels:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -40,11 +40,14 @@ def remove_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_ins
# Let's change our mind and remove it.
ifcopenshell.api.run("library.remove_reference", model, reference=reference)
"""
settings = {"reference": reference}
if file.schema != "IFC2X3":
rels = reference.LibraryRefForObjects
else:
rels = [rel for rel in file.by_type("IfcRelAssociatesLibrary") if rel.RelatingLibrary == reference]
for rel in settings["reference"].LibraryRefForObjects:
for rel in rels:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
file.remove(settings["reference"])
file.remove(reference)
@@ -15,9 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_constituent(file, constituent_set=None, material=None) -> None:
def add_constituent(
file: ifcopenshell.file, constituent_set: ifcopenshell.entity_instance, material: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
"""Adds a new constituent to a constituent set
A constituent describes how a portion of an object is made out of a
@@ -15,9 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_layer(file, layer_set=None, material=None) -> None:
def add_layer(
file: ifcopenshell.file, layer_set: ifcopenshell.entity_instance, material: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
"""Adds a new layer to a layer set
A layer represents a portion of material within a layered build up,
@@ -19,7 +19,9 @@
import ifcopenshell
def add_list_item(file, material_list=None, material=None) -> None:
def add_list_item(
file: ifcopenshell.file, material_list: ifcopenshell.entity_instance, material: ifcopenshell.entity_instance
) -> None:
"""Adds a new material in a list of materials
In IFC2X3, if you wanted an object to have multiple materials (i.e. a
@@ -15,9 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Optional
def add_material(file, name=None, category=None) -> None:
def add_material(
file: ifcopenshell.file, name: Optional[str] = None, category: Optional[str] = None
) -> ifcopenshell.entity_instance:
"""Adds a new material
A material in IFC represents a physical material, such as timber, steel,
@@ -48,7 +52,7 @@ def add_material(file, name=None, category=None) -> None:
:param name: The name of the material, typically tagged in a finishes
drawing or schedule.
:type name: str
:type name: str, optional
:param category: The category of the material.
:type category: str, optional
:return: The newly created IfcMaterial
@@ -15,9 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_material_set(file, name="Unnamed", set_type="IfcMaterialConstituentSet") -> None:
def add_material_set(
file: ifcopenshell.file, name: str = "Unnamed", set_type: str = "IfcMaterialConstituentSet"
) -> ifcopenshell.entity_instance:
"""Adds a new material set
IFC allows you to state that objects are made out of multiple materials.
@@ -19,7 +19,9 @@
import ifcopenshell.util.representation
def assign_profile(file, material_profile=None, profile=None) -> None:
def assign_profile(
file: ifcopenshell.file, material_profile: ifcopenshell.entity_instance, profile: ifcopenshell.entity_instance
) -> None:
"""Changes the profile curve of a material profile item in a profile set
In addition to changing the profile curve, it will also change the
@@ -94,7 +96,8 @@ def assign_profile(file, material_profile=None, profile=None) -> None:
class Usecase:
def execute(self):
file: ifcopenshell.file
def execute(self) -> None:
# TODO: handle composite profiles
old_profile = self.settings["material_profile"].Profile
self.settings["material_profile"].Profile = self.settings["profile"]
@@ -117,7 +120,7 @@ class Usecase:
# TODO: check remove deep
self.file.remove(old_profile)
def change_profile(self, element):
def change_profile(self, element: ifcopenshell.entity_instance) -> None:
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if not representation:
return
@@ -20,7 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element
def copy_material(file, material=None) -> None:
def copy_material(file: ifcopenshell.file, material: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Copies a material
All material psets and styles are copied. The copied material is not
@@ -48,11 +48,23 @@ def copy_material(file, material=None) -> None:
if inverse.is_a("IfcMaterialProperties"):
# Properties must not be shared between objects for convenience of authoring
inverse = ifcopenshell.util.element.copy(file, inverse)
properties = []
for pset in inverse.Properties:
properties.append(ifcopenshell.util.element.copy_deep(file, pset))
inverse.Properties = properties
inverse.Material = new
props_attribute = "Properties"
if file.schema == "IFC2X3":
if not inverse.is_a("IfcExtendedMaterialProperties"):
continue
props_attribute = "ExtendedProperties"
props = getattr(inverse, props_attribute)
if not props:
continue
copied_props = []
for pset in props:
copied_props.append(ifcopenshell.util.element.copy_deep(file, pset))
setattr(inverse, props_attribute, copied_props)
elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
inverse = ifcopenshell.util.element.copy_deep(
file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial"]
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_assigned_material(file, element=None, attributes=None) -> None:
def edit_assigned_material(file: ifcopenshell.file, element: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcMaterial
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_assigned_material(file, element=None, attributes=None) -> None:
:param element: The IfcMaterial entity you want to edit
:type element: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -38,7 +40,7 @@ def edit_assigned_material(file, element=None, attributes=None) -> None:
ifcopenshell.api.run("material.edit_assigned_material", model,
element=concrete, attributes={"Description": "40MPA concrete with broom finish"})
"""
settings = {"element": element, "attributes": attributes or {}}
settings = {"element": element, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["element"], name, value)
@@ -15,9 +15,16 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Optional, Any
def edit_constituent(file, constituent=None, attributes=None, material=None) -> None:
def edit_constituent(
file: ifcopenshell.file,
constituent: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None,
material: Optional[ifcopenshell.entity_instance] = None,
) -> None:
"""Edits the attributes of an IfcMaterialConstituent
For more information about the attributes and data types of an
@@ -15,9 +15,16 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Optional, Any
def edit_layer(file, layer=None, attributes=None, material=None) -> None:
def edit_layer(
file: ifcopenshell.file,
layer: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None,
material: Optional[ifcopenshell.entity_instance] = None,
) -> None:
"""Edits the attributes of an IfcMaterialLayer
For more information about the attributes and data types of an
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_layer_usage(file, usage=None, attributes=None) -> None:
def edit_layer_usage(file: ifcopenshell.file, usage: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcMaterialLayerSetUsage
This is typically used to change the offset from the reference line to
@@ -29,7 +31,7 @@ def edit_layer_usage(file, usage=None, attributes=None) -> None:
:param usage: The IfcMaterialLayerSetUsage entity you want to edit
:type usage: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -73,7 +75,7 @@ def edit_layer_usage(file, usage=None, attributes=None) -> None:
ifcopenshell.api.run("material.edit_layer_usage", model,
usage=rel.RelatingMaterial, attributes={"OffsetFromReferenceLine": 200})
"""
settings = {"usage": usage, "attributes": attributes or {}}
settings = {"usage": usage, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["usage"], name, value)
@@ -15,12 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_material(file, material=None, attributes=None) -> None:
def edit_material(file: ifcopenshell.file, material: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcMaterial"""
settings = {"material": material, "attributes": attributes or {}}
for name, value in settings["attributes"].items():
setattr(settings["material"], name, value)
for name, value in attributes.items():
setattr(material, name, value)
@@ -17,7 +17,17 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
def edit_profile(file, profile=None, attributes=None, profile_def=None, material=None) -> None:
from typing import Any, Optional
import ifcopenshell
def edit_profile(
file: ifcopenshell.file,
profile: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None,
profile_def: Optional[ifcopenshell.entity_instance] = None,
material: Optional[ifcopenshell.entity_instance] = None,
) -> None:
"""Edits the attributes of an IfcMaterialProfile
For more information about the attributes and data types of an
@@ -15,12 +15,14 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.geom
import ifcopenshell.util.representation
from typing import Any
def edit_profile_usage(file, usage=None, attributes=None) -> None:
def edit_profile_usage(
file: ifcopenshell.file, usage: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcMaterialProfileSetUsage
This is typically used to change the cardinal point of the profile.
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def remove_constituent(file, constituent=None) -> None:
def remove_constituent(file: ifcopenshell.file, constituent: ifcopenshell.entity_instance) -> None:
"""Removes a constituent from a constituent set
Note that it is invalid to have zero items in a set, so you should leave
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def remove_layer(file, layer=None) -> None:
def remove_layer(file: ifcopenshell.file, layer: ifcopenshell.entity_instance) -> None:
"""Removes a layer from a layer set
Note that it is invalid to have zero items in a set, so you should leave
@@ -19,7 +19,9 @@
import ifcopenshell
def remove_list_item(file, material_list=None, material_index=0) -> None:
def remove_list_item(
file: ifcopenshell.file, material_list: ifcopenshell.entity_instance, material_index: int = 0
) -> None:
"""Removes an item in an material list
Note that it is invalid to have zero items in a list, so you should leave
@@ -20,7 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_material(file, material=None) -> None:
def remove_material(file: ifcopenshell.file, material: ifcopenshell.entity_instance) -> None:
"""Removes a material
If the material is used in a material set, the corresponding layer,
@@ -62,7 +62,13 @@ def remove_material(file, material=None) -> None:
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcMaterialProperties"):
for prop in inverse.Properties or []:
if file.schema != "IFC2X3":
props = inverse.Properties
else:
# only IfcExtendedMaterialProperties have properties in IFC2X3
props = getattr(inverse, "ExtendedProperties", None)
props = props or []
for prop in props:
file.remove(prop)
file.remove(inverse)
elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
@@ -20,7 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_material_set(file, material=None) -> None:
def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_instance) -> None:
"""Removes a material set
All set items, such as layers, profiles, or constituents will also be
@@ -21,7 +21,7 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_profile(file, profile=None) -> None:
def remove_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instance) -> None:
"""Removes a profile item from a profile set
Note that it is invalid to have zero items in a set, so you should leave
@@ -15,9 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def reorder_set_item(file, material_set=None, old_index=0, new_index=0) -> None:
def reorder_set_item(
file: ifcopenshell.file, material_set: ifcopenshell.entity_instance, old_index: int = 0, new_index: int = 0
) -> None:
"""Reorders an item in a material set
In some material sets, the order have meaning, like in a layer set. In
@@ -21,15 +21,15 @@ import ifcopenshell.api
import ifcopenshell.util.element
def change_nest(file, item=None, new_parent=None) -> None:
def change_nest(
file: ifcopenshell.file, item: ifcopenshell.entity_instance, new_parent: ifcopenshell.entity_instance
) -> None:
"""Assigns a cost item to a new parent cost item"""
settings = {"item": item, "new_parent": new_parent}
if not settings["item"].Nests:
if not item.Nests:
return
nests = settings["item"].Nests[0]
nests = item.Nests[0]
related_objects = list(nests.RelatedObjects)
related_objects.remove(settings["item"])
related_objects.remove(item)
if related_objects:
nests.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", file, **{"element": nests})
@@ -41,6 +41,6 @@ def change_nest(file, item=None, new_parent=None) -> None:
ifcopenshell.api.run(
"nest.assign_object",
file,
related_objects=[settings["item"]],
relating_object=settings["new_parent"],
related_objects=[item],
relating_object=new_parent,
)
@@ -15,19 +15,18 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def reorder_nesting(file, item=None, old_index=0, new_index=0) -> None:
def reorder_nesting(
file: ifcopenshell.file, item: ifcopenshell.entity_instance, old_index: int = 0, new_index: int = 0
) -> None:
"""Reorders an item in a nesting set"""
settings = {"item": item, "old_index": old_index, "new_index": new_index}
if not settings["item"].Nests:
if not item.Nests:
return
nesting_set = settings["item"].Nests[0]
if not settings["old_index"]:
old_index = nesting_set.RelatedObjects.index(settings["item"])
else:
old_index = settings["old_index"]
nesting_set = item.Nests[0]
if not old_index:
old_index = nesting_set.RelatedObjects.index(item)
items = list(getattr(nesting_set, "RelatedObjects") or [])
items.insert(settings["new_index"], items.pop(old_index))
items.insert(new_index, items.pop(old_index))
setattr(nesting_set, "RelatedObjects", items)
@@ -19,9 +19,14 @@
import ifcopenshell
import ifcopenshell.api
from typing import Literal
def add_actor(file, actor=None, ifc_class="IfcActor") -> None:
def add_actor(
file: ifcopenshell.file,
actor: ifcopenshell.entity_instance,
ifc_class: Literal["IfcActor", "IfcOccupant"] = "IfcActor",
) -> ifcopenshell.entity_instance:
"""Adds a new actor
An actor is a person or an organisation who has a responsibility or role
@@ -15,9 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_address(file, assigned_object=None, ifc_class="IfcPostalAddress") -> None:
def add_address(
file: ifcopenshell.file, assigned_object: ifcopenshell.entity_instance, ifc_class: str = "IfcPostalAddress"
) -> ifcopenshell.entity_instance:
"""Add a new telecom or postal address to an organisation or person
A person or organisation may have associated contact details such as
@@ -17,15 +17,16 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.api
from typing import Optional
def add_application(
file,
application_developer=None,
version=None,
application_full_name="IfcOpenShell",
application_identifier="IfcOpenShell",
) -> None:
file: ifcopenshell.file,
application_developer: Optional[ifcopenshell.entity_instance] = None,
version: Optional[str] = None,
application_full_name: str = "IfcOpenShell",
application_identifier: str = "IfcOpenShell",
) -> ifcopenshell.entity_instance:
"""Adds a new application
IFC data may be associated with an authoring application to identify
@@ -46,6 +47,8 @@ def add_application(
:param application_identifier: An identification string for the
application intended for computers to read.
:type application_identifier: str, optional
:return: The newly created IfcApplication
:rtype: ifcopenshell.entity_instance
Example:
@@ -23,7 +23,7 @@ def add_person(
identification: str = "HSeldon",
family_name: str = "Seldon",
given_name: str = "Hari",
) -> None:
) -> ifcopenshell.entity_instance:
"""Adds a new person
Persons are used to identify a legal or liable representative of an
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_role(file, assigned_object=None, role="ARCHITECT") -> None:
def add_role(file: ifcopenshell.file, assigned_object: ifcopenshell.entity_instance, role: str = "ARCHITECT") -> ifcopenshell.entity_instance:
"""Adds and assigns a new role
People and organisations must play one or more roles on a project. Roles
@@ -32,7 +33,7 @@ def add_role(file, assigned_object=None, role="ARCHITECT") -> None:
be assigned to.
:type assigned_object: ifcopenshell.entity_instance
:param role: The type of role, taken from the IFC documentation for
IfcActorRole, or a custom name.
IfcActorRole, or a custom name. Defaults to "ARCHITECT".
:type role: str, optional
:return: The newly created IfcActorRole
:rtype: ifcopenshell.entity_instance
@@ -21,7 +21,9 @@ import ifcopenshell.api
import ifcopenshell.guid
def assign_actor(file, relating_actor=None, related_object=None) -> None:
def assign_actor(
file: ifcopenshell.file, relating_actor: ifcopenshell.entity_instance, related_object: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
"""Assigns an actor to an object
An actor may be assigned to objects which implies that the actor is
@@ -80,7 +82,7 @@ def assign_actor(file, relating_actor=None, related_object=None) -> None:
if settings["related_object"].HasAssignments:
for rel in settings["related_object"].HasAssignments:
if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == settings["relating_actor"]:
return
return rel
rel = None
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_actor(file, actor=None, attributes=None) -> None:
def edit_actor(file: ifcopenshell.file, actor: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcActor
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_actor(file, actor=None, attributes=None) -> None:
:param actor: The IfcActor entity you want to edit
:type actor: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -47,7 +49,7 @@ def edit_actor(file, actor=None, attributes=None) -> None:
ifcopenshell.api.run("actor.edit_actor", model,
actor=actor, attributes={"Description": "Responsible for buildings A, B, and C."})
"""
settings = {"actor": actor, "attributes": attributes or {}}
settings = {"actor": actor, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["actor"], name, value)
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_address(file, address=None, attributes=None) -> None:
def edit_address(file: ifcopenshell.file, address: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcAddress
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_address(file, address=None, attributes=None) -> None:
:param address: The IfcAddress entity you want to edit
:type address: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -49,7 +51,7 @@ def edit_address(file, address=None, attributes=None) -> None:
"ElectronicMailAddresses": ["bobthebuilder@example.com"],
"WWWHomePageURL": "https://thinkmoult.com"})
"""
settings = {"address": address, "attributes": attributes or {}}
settings = {"address": address, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["address"], name, value)
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_organisation(file, organisation=None, attributes=None) -> None:
def edit_organisation(file: ifcopenshell.file, organisation: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcOrganization
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_organisation(file, organisation=None, attributes=None) -> None:
:param organisation: The IfcOrganization entity you want to edit
:type organisation: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -39,7 +41,7 @@ def edit_organisation(file, organisation=None, attributes=None) -> None:
ifcopenshell.api.run("owner.edit_organisation", model, organisation=organisation,
attributes={"name": "Architects Without Ballpens"})
"""
settings = {"organisation": organisation, "attributes": attributes or {}}
settings = {"organisation": organisation, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["organisation"], name, value)
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_person(file, person=None, attributes=None) -> None:
def edit_person(file: ifcopenshell.file, person: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcPerson
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_person(file, person=None, attributes=None) -> None:
:param person: The IfcPerson entity you want to edit
:type person: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -39,7 +41,7 @@ def edit_person(file, person=None, attributes=None) -> None:
ifcopenshell.api.run("owner.edit_person", model, person=person,
attributes={"MiddleNames": ["The"], "FamilyName": "Builder"})
"""
settings = {"person": person, "attributes": attributes or {}}
settings = {"person": person, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["person"], name, value)
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_role(file, role=None, attributes=None) -> None:
def edit_role(file: ifcopenshell.file, role: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcActorRole
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_role(file, role=None, attributes=None) -> None:
:param role: The IfcActorRole entity you want to edit
:type role: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -43,7 +45,7 @@ def edit_role(file, role=None, attributes=None) -> None:
# But Bob is not an architect
ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "CONSTRUCTIONMANAGER"})
"""
settings = {"role": role, "attributes": attributes or {}}
settings = {"role": role, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["role"], name, value)
@@ -20,7 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_actor(file, actor=None) -> None:
def remove_actor(file: ifcopenshell.file, actor: ifcopenshell.entity_instance) -> None:
"""Removes an actor
:param actor: The IfcActor to remove.
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def remove_address(file, address=None) -> None:
def remove_address(file: ifcopenshell.file, address: ifcopenshell.entity_instance) -> None:
"""Removes an address
Naturally, any organisations or people using that address will have the
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def remove_application(file, application=None) -> None:
def remove_application(file: ifcopenshell.file, application: ifcopenshell.entity_instance) -> None:
"""Removes an application
Warning: removing an application may invalidate ownership histories.
@@ -19,7 +19,7 @@
import ifcopenshell.api
def remove_organisation(file, organisation=None) -> None:
def remove_organisation(file: ifcopenshell.file, organisation: ifcopenshell.entity_instance) -> None:
"""Remove an organisation
All roles and addresses assigned to the organisation will also be
@@ -19,11 +19,13 @@
import ifcopenshell.api
def remove_person(file, person=None) -> None:
def remove_person(file: ifcopenshell.file, person: ifcopenshell.entity_instance) -> None:
"""Remove an person
All roles and addresses assigned to the person will also be
removed.
In IFC2X3 will also remove related inventories if `person` was
the only responsile person for them.
:param person: The IfcPerson to remove
:type person: ifcopenshell.entity_instance
@@ -52,7 +54,9 @@ def remove_person(file, person=None) -> None:
inverse.Creators = None
elif inverse.is_a("IfcInventory"):
if inverse.ResponsiblePersons == (settings["person"],):
inverse.ResponsiblePersons = None
# in IFC2X3 ResponsiblePersons is not optional and without it IfcInventory is not valid
if file.schema == "IFC2X3":
ifcopenshell.api.run("root.remove_product", file, product=inverse)
elif inverse.is_a("IfcDocumentInformation"):
if inverse.Editors == (settings["person"],):
inverse.Editors = None
@@ -19,7 +19,9 @@
import ifcopenshell.api
def remove_person_and_organisation(file, person_and_organisation=None) -> None:
def remove_person_and_organisation(
file: ifcopenshell.file, person_and_organisation: ifcopenshell.entity_instance
) -> None:
"""Removes a person and organisation
Note that the underlying person and organisation is not removed, only
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def remove_role(file, role=None) -> None:
def remove_role(file: ifcopenshell.file, role: ifcopenshell.entity_instance) -> None:
"""Removes a role
People and organisations using the role will be untouched. This may
@@ -21,7 +21,9 @@ import ifcopenshell.api
import ifcopenshell.util.element
def unassign_actor(file, relating_actor=None, related_object=None) -> None:
def unassign_actor(
file: ifcopenshell.file, relating_actor: ifcopenshell.entity_instance, related_object: ifcopenshell.entity_instance
) -> None:
"""Unassigns an actor to an object
This means that the actor is no longer responsible for the object.
@@ -30,9 +32,8 @@ def unassign_actor(file, relating_actor=None, related_object=None) -> None:
:type relating_actor: ifcopenshell.entity_instance
:param related_object: The object the actor is responsible for.
:type related_object: ifcopenshell.entity_instance
:return: The updated IfcRelAssignsToActor relationship or none if there
is no more valid relationship.
:rtype: None, ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -72,4 +73,3 @@ def unassign_actor(file, relating_actor=None, related_object=None) -> None:
related_objects.remove(settings["related_object"])
rel.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
return rel
@@ -17,9 +17,12 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.unit
from typing import Optional
def add_arbitrary_profile(file, profile=None, name=None) -> None:
def add_arbitrary_profile(
file: ifcopenshell.file, profile: list[tuple[float, float]], name: Optional[str] = None
) -> ifcopenshell.entity_instance:
"""Adds a new arbitrary polyline-based profile
The profile is represented as a polyline defined by a list of
@@ -30,7 +33,7 @@ def add_arbitrary_profile(file, profile=None, name=None) -> None:
identical.
:param profile: A list of coordinates
:type profile: list[list[float]]
:type profile: list[tuple[float, float]]
:param name: If the profile is semantically significant (i.e. to be
managed and reused by the user) then it must be named. Otherwise,
this may be left as none.
@@ -17,9 +17,15 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.unit
from typing import Optional
def add_arbitrary_profile_with_voids(file, outer_profile=None, inner_profiles=None, name=None) -> None:
def add_arbitrary_profile_with_voids(
file: ifcopenshell.file,
outer_profile: list[tuple[float, float]],
inner_profiles: list[list[tuple[float, float]]],
name: Optional[str] = None,
) -> ifcopenshell.entity_instance:
"""Adds a new arbitrary polyline-based profile with voids
The outer profile is represented as a polyline defined by a list of
@@ -35,9 +41,9 @@ def add_arbitrary_profile_with_voids(file, outer_profile=None, inner_profiles=No
provided in SI meters.
:param outer_profile: A list of coordinates
:type profile: list[float]
:type profile: list[tuple[float, float]]
:param inner_profiles: A list of polylines
:type profile: list[list[float]]
:type profile: list[list[tuple[float, float]]]
:param name: If the profile is semantically significant (i.e. to be
managed and reused by the user) then it must be named. Otherwise,
this may be left as none.
@@ -15,9 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_parameterized_profile(file, ifc_class=None) -> None:
def add_parameterized_profile(file: ifcopenshell.file, ifc_class: str) -> ifcopenshell.entity_instance:
"""Adds a new parameterised profile
IFC offers parameterised profiles for common standardised hot roll
@@ -15,9 +15,11 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def edit_profile(file, profile=None, attributes=None) -> None:
def edit_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcProfileDef
For more information about the attributes and data types of an
@@ -26,7 +28,7 @@ def edit_profile(file, profile=None, attributes=None) -> None:
:param profile: The IfcProfileDef entity you want to edit
:type profile: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:type attributes: dict
:return: None
:rtype: None
@@ -41,7 +43,7 @@ def edit_profile(file, profile=None, attributes=None) -> None:
ifcopenshell.api.run("profile.edit_profile", model,
profile=circle, attributes={"ProfileName": "1000mm Dia"})
"""
settings = {"profile": profile, "attributes": attributes or {}}
settings = {"profile": profile, "attributes": attributes}
for name, value in settings["attributes"].items():
setattr(settings["profile"], name, value)
@@ -20,7 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element
def remove_profile(file, profile=None) -> None:
def remove_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instance) -> None:
"""Removes a profile
:param profile: The IfcProfileDef to remove.
@@ -19,7 +19,8 @@
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.owner.settings
from typing import Optional
import ifcopenshell.util.element
from typing import Optional, Any, Union
def append_asset(
@@ -124,11 +125,16 @@ def append_asset(
class Usecase:
file: ifcopenshell.file
settings: dict[str, Any]
def execute(self):
# mapping of old element ids to new elements
self.added_elements: dict[int, ifcopenshell.entity_instance] = {}
self.reuse_identities: dict[int, ifcopenshell.entity_instance] = self.settings["reuse_identities"]
self.whitelisted_inverse_attributes = {}
self.base_material_class = "IfcMaterial" if self.file.schema == "IFC2X3" else "IfcMaterialDefinition"
if self.settings["element"].is_a("IfcTypeProduct"):
self.target_class = "IfcTypeProduct"
return self.append_type_product()
@@ -179,7 +185,7 @@ class Usecase:
def append_type_product(self):
self.whitelisted_inverse_attributes = {
"IfcObjectDefinition": ["HasAssociations"],
"IfcMaterialDefinition": ["HasExternalReferences", "HasProperties", "HasRepresentation"],
self.base_material_class: ["HasExternalReferences", "HasProperties", "HasRepresentation"],
"IfcRepresentationItem": ["StyledByItem"],
}
self.existing_contexts = self.file.by_type("IfcGeometricRepresentationContext")
@@ -192,7 +198,7 @@ class Usecase:
"IfcObjectDefinition": ["HasAssociations"],
"IfcObject": ["IsDefinedBy.IfcRelDefinesByProperties"],
"IfcElement": ["HasOpenings"],
"IfcMaterialDefinition": ["HasExternalReferences", "HasProperties", "HasRepresentation"],
self.base_material_class: ["HasExternalReferences", "HasProperties", "HasRepresentation"],
"IfcRepresentationItem": ["StyledByItem"],
}
self.existing_contexts = self.file.by_type("IfcGeometricRepresentationContext")
@@ -221,7 +227,7 @@ class Usecase:
return element
def add_element(self, element):
def add_element(self, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
if element.id() == 0:
return
existing_element = self.get_existing_element(element)
@@ -260,7 +266,7 @@ class Usecase:
elif value:
return True
def check_inverses(self, element):
def check_inverses(self, element: ifcopenshell.entity_instance) -> None:
for source_class, attributes in self.whitelisted_inverse_attributes.items():
if not element.is_a(source_class):
continue
@@ -61,6 +61,9 @@ def add_pset(file: ifcopenshell.file, product: ifcopenshell.entity_instance, nam
that prefix. It is recommended to use your own prefix tailored to
your project, company, or local government requirement.
:type name: str
:raises TypeError: If `product` class doesn't support adding a pset.
:return: The newly created IfcPropertySet
:rtype: ifcopenshell.entity_instance
@@ -154,3 +157,5 @@ def add_pset(file: ifcopenshell.file, product: ifcopenshell.entity_instance, nam
kwargs["Name"] = settings["name"]
return file.create_entity("IfcProfileProperties", **kwargs)
raise TypeError(f"Class '{settings['product'].is_a(True)}' doesn't support adding a property set.")
@@ -378,18 +378,29 @@ class Usecase:
def assign_new_properties(self, props: ifcopenshell.entity_instance) -> None:
if hasattr(self.settings["pset"], "HasProperties"):
self.settings["pset"].HasProperties = props
# Material / Profile properties
elif hasattr(self.settings["pset"], "Properties"):
self.settings["pset"].Properties = props
# IFC2X3 IfcMaterialProperties
elif self.settings["pset"].is_a("IfcMaterialProperties"):
self.settings["pset"].ExtendedProperties = props
def get_properties(self) -> list[ifcopenshell.entity_instance]:
"""
Returns list of existing properties
"""
if hasattr(self.settings["pset"], "HasProperties"):
return self.settings["pset"].HasProperties or []
if (props := getattr(self.settings["pset"], "HasProperties", ...)) is not ...:
return props or []
elif hasattr(self.settings["pset"], "Properties"): # For IfcMaterialProperties
return self.settings["pset"].Properties or []
# Material / Profile properties
elif (props := getattr(self.settings["pset"], "Properties", ...)) is not ...:
return props or []
# IFC2X3 IfcMaterialProperties
elif (props := getattr(self.settings["pset"], "ExtendedProperties", ...)) is not ...:
return props or []
raise TypeError(f"'{self.settings['pset']}' is not a valid pset")
@@ -19,6 +19,7 @@
import datetime
from re import findall
from dateutil import parser
from typing import Literal, Union, Any
try:
import isodate
@@ -104,7 +105,18 @@ def readable_ifc_duration(string):
return final_string
def datetime2ifc(dt, ifc_type):
def datetime2ifc(
dt: Union[datetime.date, str],
ifc_type: Literal[
"IfcDuration",
"IfcTimeStamp",
"IfcDateTime",
"IfcDate",
"IfcTime",
"IfcCalendarDate",
"IfcLocalTime",
],
) -> Union[int, str, dict[str, Any]]:
if isinstance(dt, str):
if ifc_type == "IfcDuration":
return dt
@@ -145,6 +157,7 @@ def datetime2ifc(dt, ifc_type):
"MinuteComponent": dt.minute,
"SecondComponent": dt.second,
}
raise TypeError(f"Unsupported ifc_type for conversion from datetime.datetime = {ifc_type}.")
def string_to_date(string):
@@ -37,3 +37,7 @@ class TestAssignLayer(test.bootstrap.IFC4):
ifcopenshell.api.run("layer.assign_layer", self.file, items=items[2:], layer=layer)
assert len(layer.AssignedItems) == 4
assert set(layer.AssignedItems) == set(items)
class TestAssignLayerIFC2X3(test.bootstrap.IFC2X3, TestAssignLayer):
pass
@@ -37,3 +37,7 @@ class TestUnassignLayer(test.bootstrap.IFC4):
ifcopenshell.api.run("layer.assign_layer", self.file, items=items, layer=layer)
ifcopenshell.api.run("layer.unassign_layer", self.file, items=items, layer=layer)
assert not self.file.by_type("IfcPresentationLayerAssignment")
class TestUnassignLayerIFC2X3(test.bootstrap.IFC2X3, TestUnassignLayer):
pass
@@ -25,3 +25,7 @@ class TestAddLibrary(test.bootstrap.IFC4):
library = ifcopenshell.api.run("library.add_library", self.file, name="Name")
assert library.is_a("IfcLibraryInformation")
assert library.Name == "Name"
class TestAddLibraryIFC2X3(test.bootstrap.IFC2X3, TestAddLibrary):
pass
@@ -20,28 +20,6 @@ import test.bootstrap
import ifcopenshell.api
def validate_ifc_file(ifc_file: ifcopenshell.file, use_json=True):
import ifcopenshell
import ifcopenshell.validate
if use_json:
logger = ifcopenshell.validate.json_logger()
else:
import logging
logger = logging.getLogger("validate")
logger.setLevel(logging.DEBUG)
ifcopenshell.validate.validate(ifc_file, logger, express_rules=True)
if use_json:
if logger.statements:
from pprint import pprint
pprint(logger.statements)
else:
print("IFC is completely valid.")
class TestAssignReference(test.bootstrap.IFC4):
def test_assigning_a_reference(self):
reference = self.file.createIfcLibraryReference()
@@ -17,26 +17,41 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import test.bootstrap
import datetime
import ifcopenshell.api
import ifcopenshell.util.date
class TestEditLibrary(test.bootstrap.IFC4):
def test_editing_a_library(self):
library = self.file.createIfcLibraryInformation()
dt = datetime.datetime.now()
attributes = {
"Name": "Name",
"Version": "Version",
"VersionDate": dt,
}
if self.file.schema != "IFC2X3":
attributes["Location"] = "Location"
attributes["Description"] = "Description"
ifcopenshell.api.run(
"library.edit_library",
self.file,
library=library,
attributes={
"Name": "Name",
"Version": "Version",
"VersionDate": "VersionDate",
"Location": "Location",
"Description": "Description",
},
attributes=attributes,
)
assert library.Name == "Name"
assert library.Version == "Version"
assert library.VersionDate == "VersionDate"
assert library.Location == "Location"
assert library.Description == "Description"
if self.file.schema != "IFC2X3":
assert library.VersionDate == ifcopenshell.util.date.datetime2ifc(dt, "IfcDateTime")
assert library.Location == "Location"
assert library.Description == "Description"
else:
info = library.VersionDate.get_info()
del info["id"], info["type"]
assert info == ifcopenshell.util.date.datetime2ifc(dt, "IfcCalendarDate")
class TestEditLibraryIFC2X3(test.bootstrap.IFC2X3, TestEditLibrary):
pass
@@ -23,20 +23,28 @@ import ifcopenshell.api
class TestEditReference(test.bootstrap.IFC4):
def test_editing_a_reference(self):
reference = self.file.createIfcLibraryReference()
attributes = {
"Location": "Location",
"Identification" if self.file.schema != "IFC2X3" else "ItemReference": "Identification",
"Name": "Name",
}
if self.file.schema != "IFC2X3":
attributes["Description"] = "Description"
attributes["Language"] = "Language"
ifcopenshell.api.run(
"library.edit_reference",
self.file,
reference=reference,
attributes={
"Location": "Location",
"Identification": "Identification",
"Name": "Name",
"Description": "Description",
"Language": "Language",
},
attributes=attributes,
)
assert reference.Location == "Location"
assert reference.Identification == "Identification"
# 1 IfcExternalReference Identification(>IFC2X3) / ItemReference (IFC2X3)
assert reference[1] == "Identification"
assert reference.Name == "Name"
assert reference.Description == "Description"
assert reference.Language == "Language"
if self.file.schema != "IFC2X3":
assert reference.Description == "Description"
assert reference.Language == "Language"
class TestEditReferenceIFC2X3(test.bootstrap.IFC2X3, TestEditReference):
pass
@@ -27,11 +27,21 @@ class TestRemoveLibrary(test.bootstrap.IFC4):
assert len(self.file.by_type("IfcLibraryInformation")) == 0
def test_removing_a_library_and_all_references(self):
library = self.file.createIfcLibraryInformation()
reference1 = self.file.createIfcLibraryReference(ReferencedLibrary=library)
reference2 = self.file.createIfcLibraryReference(ReferencedLibrary=library)
if self.file.schema != "IFC2X3":
library = self.file.createIfcLibraryInformation()
reference1 = self.file.createIfcLibraryReference(ReferencedLibrary=library)
reference2 = self.file.createIfcLibraryReference(ReferencedLibrary=library)
else:
reference1 = self.file.createIfcLibraryReference()
reference2 = self.file.createIfcLibraryReference()
library = self.file.createIfcLibraryInformation(LibraryReference=[reference1, reference2])
self.file.createIfcRelAssociatesLibrary(GlobalId="foo", RelatingLibrary=library)
self.file.createIfcRelAssociatesLibrary(GlobalId="bar", RelatingLibrary=reference1)
ifcopenshell.api.run("library.remove_library", self.file, library=library)
assert len(self.file.by_type("IfcLibraryReference")) == 0
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
class TestRemoveLibraryIFC2X3(test.bootstrap.IFC2X3, TestRemoveLibrary):
pass
@@ -28,3 +28,7 @@ class TestRemoveReference(test.bootstrap.IFC4):
ifcopenshell.api.run("library.remove_reference", self.file, reference=reference)
assert len(self.file.by_type("IfcLibraryReference")) == 0
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
class TestRemoveReferenceIFC2X3(test.bootstrap.IFC2X3, TestRemoveReference):
pass
@@ -20,12 +20,19 @@ import test.bootstrap
import ifcopenshell.api
class TestAddMaterialSet(test.bootstrap.IFC4):
class TestAddMaterialSetIFC2X3(test.bootstrap.IFC2X3):
def test_add_layer_set(self):
material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialLayerSet")
assert material.LayerSetName == "Unnamed"
assert material.is_a("IfcMaterialLayerSet")
def test_add_list(self):
material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialList")
assert material.is_a("IfcMaterialList")
class TestAddMaterialSetIFC4(test.bootstrap.IFC4, TestAddMaterialSetIFC2X3):
# entities added in IFC4
def test_add_profile_set(self):
material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialProfileSet")
assert material.Name == "Unnamed"
@@ -35,7 +42,3 @@ class TestAddMaterialSet(test.bootstrap.IFC4):
material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialConstituentSet")
assert material.Name == "Unnamed"
assert material.is_a("IfcMaterialConstituentSet")
def test_add_list(self):
material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialList")
assert material.is_a("IfcMaterialList")
@@ -33,19 +33,31 @@ class TestCopyMaterial(test.bootstrap.IFC4):
material = ifcopenshell.api.run("material.add_material", self.file, name="CON01")
ifcopenshell.api.run("material.assign_material", self.file, products=[element], material=material)
new = ifcopenshell.api.run("material.copy_material", self.file, material=material)
assert material.AssociatedTo
assert not new.AssociatedTo
assert ifcopenshell.util.element.get_elements_by_material(self.file, material)
assert not ifcopenshell.util.element.get_elements_by_material(self.file, new)
def test_copy_a_material_with_properties(self):
def get_material_props(material: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
if self.file.schema != "IFC2X3":
return material.HasProperties
return [i for i in self.file.get_inverse(material) if i.is_a("IfcMaterialProperties")]
material = ifcopenshell.api.run("material.add_material", self.file, name="CON01")
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=material, name="Foo_Bar")
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"foo": "bar"})
new = ifcopenshell.api.run("material.copy_material", self.file, material=material)
assert new.Name == "CON01"
assert len(self.file.by_type("IfcMaterial")) == 2
assert new.HasProperties[0] != material.HasProperties[0]
assert new.HasProperties[0].Properties[0] != material.HasProperties[0].Properties[0]
assert ifcopenshell.util.element.get_pset(new, "Foo_Bar", "foo") == "bar"
assert len(self.file.by_type("IfcMaterialProperties")) == 2
props_old = get_material_props(material)[0]
props_new = get_material_props(new)[0]
assert props_old != props_new
if self.file.schema != "IFC2X3":
assert props_old.Properties[0] != props_new.Properties[0]
assert ifcopenshell.util.element.get_pset(new, "Foo_Bar", "foo") == "bar"
else:
assert props_old.ExtendedProperties[0] != props_new.ExtendedProperties[0]
assert props_new.ExtendedProperties[0].NominalValue.wrappedValue == "bar"
def test_copy_a_material_with_a_style_representation(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
@@ -59,3 +71,6 @@ class TestCopyMaterial(test.bootstrap.IFC4):
assert new.HasRepresentation[0] != material.HasRepresentation[0]
assert new.HasRepresentation[0].Representations[0] != material.HasRepresentation[0].Representations[0]
assert new.HasRepresentation[0].Representations[0].ContextOfItems == context
class TestCopyMaterialIFC2X3(test.bootstrap.IFC2X3, TestCopyMaterial):
pass
@@ -20,7 +20,7 @@ import test.bootstrap
import ifcopenshell.api
class TestRemoveMaterial(test.bootstrap.IFC4):
class TestRemoveMaterialIFC2X3(test.bootstrap.IFC2X3):
def test_removing_material(self):
material = ifcopenshell.api.run("material.add_material", self.file)
ifcopenshell.api.run("material.remove_material", self.file, material=material)
@@ -37,9 +37,7 @@ class TestRemoveMaterial(test.bootstrap.IFC4):
def test_removing_material_in_layer(self):
wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", self.file)
material_set = ifcopenshell.api.run(
"material.add_material_set", self.file, set_type="IfcMaterialLayerSet"
)
material_set = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialLayerSet")
ifcopenshell.api.run("material.add_layer", self.file, layer_set=material_set, material=material)
ifcopenshell.api.run("material.assign_material", self.file, products=[wall], material=material_set)
assert len(self.file.by_type("IfcMaterialLayerSet")[0].MaterialLayers) == 1
@@ -48,40 +46,10 @@ class TestRemoveMaterial(test.bootstrap.IFC4):
assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 1
assert len(self.file.by_type("IfcMaterialLayerSet")[0].MaterialLayers) == 0
def test_removing_material_in_profile(self):
wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", self.file)
material_set = ifcopenshell.api.run(
"material.add_material_set", self.file, set_type="IfcMaterialProfileSet"
)
ifcopenshell.api.run("material.add_profile", self.file, profile_set=material_set, material=material)
ifcopenshell.api.run("material.assign_material", self.file, products=[wall], material=material_set)
assert len(self.file.by_type("IfcMaterialProfileSet")[0].MaterialProfiles) == 1
ifcopenshell.api.run("material.remove_material", self.file, material=material)
assert len(self.file.by_type("IfcMaterial")) == 0
assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 1
assert len(self.file.by_type("IfcMaterialProfileSet")[0].MaterialProfiles) == 0
def test_removing_material_in_constituent(self):
wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", self.file)
material_set = ifcopenshell.api.run(
"material.add_material_set", self.file, set_type="IfcMaterialConstituentSet"
)
ifcopenshell.api.run("material.add_constituent", self.file, constituent_set=material_set, material=material)
ifcopenshell.api.run("material.assign_material", self.file, products=[wall], material=material_set)
assert len(self.file.by_type("IfcMaterialConstituentSet")[0].MaterialConstituents) == 1
ifcopenshell.api.run("material.remove_material", self.file, material=material)
assert len(self.file.by_type("IfcMaterial")) == 0
assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 1
assert self.file.by_type("IfcMaterialConstituentSet")[0].MaterialConstituents is None
def test_removing_material_in_list(self):
wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", self.file)
material_set = ifcopenshell.api.run(
"material.add_material_set", self.file, set_type="IfcMaterialList"
)
material_set = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialList")
ifcopenshell.api.run("material.add_list_item", self.file, material_list=material_set, material=material)
ifcopenshell.api.run("material.assign_material", self.file, products=[wall], material=material_set)
assert len(self.file.by_type("IfcMaterialList")[0].Materials) == 1
@@ -108,10 +76,44 @@ class TestRemoveMaterial(test.bootstrap.IFC4):
assert len(self.file.by_type("IfcSurfaceStyle")) == 1
def test_removing_a_material_with_properties(self):
def get_material_props(material: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
if self.file.schema != "IFC2X3":
return material.HasProperties
return [i for i in self.file.get_inverse(material) if i.is_a("IfcMaterialProperties")]
material = ifcopenshell.api.run("material.add_material", self.file)
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=material, name="Foo_Bar")
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Bar"})
assert material.HasProperties
assert get_material_props(material)
ifcopenshell.api.run("material.remove_material", self.file, material=material)
assert len(self.file.by_type("IfcMaterialProperties")) == 0
assert len(self.file.by_type("IfcPropertySingleValue")) == 0
class TestRemoveMaterialIFC4(test.bootstrap.IFC4, TestRemoveMaterialIFC2X3):
# IfcMaterialProfileSet added in IFC4
def test_removing_material_in_profile(self):
wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", self.file)
material_set = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialProfileSet")
ifcopenshell.api.run("material.add_profile", self.file, profile_set=material_set, material=material)
ifcopenshell.api.run("material.assign_material", self.file, products=[wall], material=material_set)
assert len(self.file.by_type("IfcMaterialProfileSet")[0].MaterialProfiles) == 1
ifcopenshell.api.run("material.remove_material", self.file, material=material)
assert len(self.file.by_type("IfcMaterial")) == 0
assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 1
assert len(self.file.by_type("IfcMaterialProfileSet")[0].MaterialProfiles) == 0
def test_removing_material_in_constituent(self):
wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", self.file)
material_set = ifcopenshell.api.run(
"material.add_material_set", self.file, set_type="IfcMaterialConstituentSet"
)
ifcopenshell.api.run("material.add_constituent", self.file, constituent_set=material_set, material=material)
ifcopenshell.api.run("material.assign_material", self.file, products=[wall], material=material_set)
assert len(self.file.by_type("IfcMaterialConstituentSet")[0].MaterialConstituents) == 1
ifcopenshell.api.run("material.remove_material", self.file, material=material)
assert len(self.file.by_type("IfcMaterial")) == 0
assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 1
assert self.file.by_type("IfcMaterialConstituentSet")[0].MaterialConstituents is None
@@ -20,7 +20,7 @@ import test.bootstrap
import ifcopenshell.api
class TestRemoveMaterialSet(test.bootstrap.IFC4):
class TestRemoveMaterialSetIFC2X3(test.bootstrap.IFC2X3):
def test_removing_material_set(self):
material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialLayerSet")
ifcopenshell.api.run("material.remove_material_set", self.file, material=material)
@@ -43,6 +43,9 @@ class TestRemoveMaterialSet(test.bootstrap.IFC4):
assert len(self.file.by_type("IfcMaterialLayer")) == 0
assert len(self.file.by_type("IfcMaterial")) == 1
class TestRemoveMaterialSetIFC4(test.bootstrap.IFC4, TestRemoveMaterialSetIFC2X3):
# IFC2X3 doesn't support adding a pset to IfcMaterialLayerSet
def test_removing_a_material_set_with_properties(self):
material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialLayerSet")
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=material, name="Foo_Bar")
@@ -32,3 +32,7 @@ class TestAddActor(test.bootstrap.IFC4):
actor = ifcopenshell.api.run("owner.add_actor", self.file, ifc_class="IfcOccupant", actor=person)
assert actor.is_a() == "IfcOccupant"
assert actor.TheActor == person
class TestAddActorIFC2X3(test.bootstrap.IFC2X3, TestAddActor):
pass
@@ -48,3 +48,7 @@ class TestAddAddress(test.bootstrap.IFC4):
assert postal.Purpose == telecom.Purpose == "OFFICE"
assert postal in organisation.Addresses
assert telecom in organisation.Addresses
class TestAddAddressIFC2X3(test.bootstrap.IFC2X3, TestAddAddress):
pass
@@ -28,7 +28,8 @@ class TestAddApplication(test.bootstrap.IFC4):
assert application.ApplicationFullName == "IfcOpenShell"
assert application.ApplicationIdentifier == "IfcOpenShell"
assert developer.is_a("IfcOrganization")
assert developer.Identification == "IfcOpenShell"
# 0 IfcOrganization Identification(>IFC2X3) / Id (IFC2X3)
assert developer[0] == "IfcOpenShell"
assert developer.Name == "IfcOpenShell"
assert (
developer.Description
@@ -40,3 +41,7 @@ class TestAddApplication(test.bootstrap.IFC4):
assert developer.Addresses[0].Purpose == "USERDEFINED"
assert developer.Addresses[0].UserDefinedPurpose == "WEBPAGE"
assert developer.Addresses[0].WWWHomePageURL == "https://ifcopenshell.org"
class TestAddApplicationIFC2X3(test.bootstrap.IFC2X3, TestAddApplication):
pass
@@ -23,5 +23,10 @@ import ifcopenshell.api
class TestAddOrganisation(test.bootstrap.IFC4):
def test_adding_an_organisation(self):
org = ifcopenshell.api.run("owner.add_organisation", self.file, identification="Id", name="Name")
assert org.Identification == "Id"
# 0 IfcOrganization Identification(>IFC2X3) / Id (IFC2X3)
assert org[0] == "Id"
assert org.Name == "Name"
class TestAddOrganisationIFC2X3(test.bootstrap.IFC2X3, TestAddOrganisation):
pass
@@ -29,6 +29,11 @@ class TestAddPerson(test.bootstrap.IFC4):
family_name="FamilyName",
given_name="GivenName",
)
assert person.Identification == "Identification"
# 0 IfcPerson Identification(>IFC2X3) / Id (IFC2X3)
assert person[0] == "Identification"
assert person.FamilyName == "FamilyName"
assert person.GivenName == "GivenName"
class TestAddPersonIFC2X3(test.bootstrap.IFC2X3, TestAddPerson):
pass
@@ -27,3 +27,7 @@ class TestAddPersonAndOrganisation(test.bootstrap.IFC4):
person_and_organisation = ifcopenshell.api.run(
"owner.add_person_and_organisation", self.file, person=person, organisation=organisation
)
class TestAddPersonAndOrganisationIFC2X3(test.bootstrap.IFC2X3, TestAddPersonAndOrganisation):
pass
@@ -34,3 +34,7 @@ class TestAddRole(test.bootstrap.IFC4):
assert role.is_a("IfcActorRole")
assert role.Role == "ARCHITECT"
assert organisation.Roles == (role,)
class TestAddRoleIFC2X3(test.bootstrap.IFC2X3, TestAddRole):
pass
@@ -20,7 +20,7 @@ import test.bootstrap
import ifcopenshell.api
class TestAssignProduct(test.bootstrap.IFC4):
class TestAssignActor(test.bootstrap.IFC4):
def test_assigning_an_actor(self):
wall = self.file.createIfcWall()
wall2 = self.file.createIfcWall()
@@ -36,3 +36,7 @@ class TestAssignProduct(test.bootstrap.IFC4):
ifcopenshell.api.run("owner.assign_actor", self.file, relating_actor=actor, related_object=wall)
ifcopenshell.api.run("owner.assign_actor", self.file, relating_actor=actor, related_object=wall)
assert actor.IsActingUpon[0].RelatedObjects == (wall,)
class TestAssignActorIFC2X3(test.bootstrap.IFC2X3, TestAssignActor):
pass
@@ -17,25 +17,36 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import time
import pytest
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.api.owner.settings
class TestCreateOwnerHistory(test.bootstrap.IFC4):
def test_creating_nothing_if_no_user_or_application_is_available(self):
history = ifcopenshell.api.run("owner.create_owner_history", self.file)
assert history is None
if self.file.schema != "IFC2X3":
history = ifcopenshell.api.run("owner.create_owner_history", self.file)
assert history is None
else:
ifcopenshell.api.owner.settings.factory_reset()
# create new file as bootstrap is creating users in ifc2x3 by default
file = ifcopenshell.file(schema="IFC2X3")
with pytest.raises(Exception) as e:
ifcopenshell.api.run("owner.create_owner_history", file)
assert "Please create a user to continue" in str(e.value)
ifcopenshell.api.owner.settings.restore()
def test_creating_a_history_using_a_specified_user_and_application(self):
old_get_user = ifcopenshell.api.owner.settings.get_user
old_get_application = ifcopenshell.api.owner.settings.get_application
ifcopenshell.api.owner.settings.factory_reset()
user = self.file.createIfcPersonAndOrganization()
application = self.file.createIfcApplication()
ifcopenshell.api.owner.settings.get_user = lambda x: user
ifcopenshell.api.owner.settings.get_application = lambda x: application
history = ifcopenshell.api.run("owner.create_owner_history", self.file)
ifcopenshell.api.owner.settings.get_user = old_get_user
ifcopenshell.api.owner.settings.get_application = old_get_application
ifcopenshell.api.owner.settings.restore()
assert history.is_a("IfcOwnerHistory")
assert history.OwningUser == user
assert history.OwningApplication == application
@@ -45,3 +56,7 @@ class TestCreateOwnerHistory(test.bootstrap.IFC4):
assert history.LastModifyingUser == user
assert history.LastModifyingApplication == application
assert abs(time.time() - history.CreationDate) < 5
class TestCreateOwnerHistoryIFC2X3(test.bootstrap.IFC2X3, TestCreateOwnerHistory):
pass
@@ -52,3 +52,7 @@ class TestEditActor(test.bootstrap.IFC4):
assert actor.Description == "Description"
assert actor.ObjectType == "ObjectType"
assert actor.PredefinedType == "TENANT"
class TestEditActorIFC2X3(test.bootstrap.IFC2X3, TestEditActor):
pass
@@ -53,21 +53,24 @@ class TestEditAddress(test.bootstrap.IFC4):
def test_editing_a_telecom_address(self):
address = self.file.createIfcTelecomAddress()
attributes = {
"Purpose": "OFFICE",
"Description": "Description",
"UserDefinedPurpose": "UserDefinedPurpose",
"TelephoneNumbers": ["Telephone", "Numbers"],
"FacsimileNumbers": ["Facsimile", "Numbers"],
"PagerNumber": "PagerNumber",
"ElectronicMailAddresses": ["Electronic", "Mail", "Addresses"],
"WWWHomePageURL": "WWWHomePageURL",
}
if self.file.schema != "IFC2X3":
attributes["MessagingIDs"] = ["Messaging", "IDs"]
ifcopenshell.api.run(
"owner.edit_address",
self.file,
address=address,
attributes={
"Purpose": "OFFICE",
"Description": "Description",
"UserDefinedPurpose": "UserDefinedPurpose",
"TelephoneNumbers": ["Telephone", "Numbers"],
"FacsimileNumbers": ["Facsimile", "Numbers"],
"PagerNumber": "PagerNumber",
"ElectronicMailAddresses": ["Electronic", "Mail", "Addresses"],
"WWWHomePageURL": "WWWHomePageURL",
"MessagingIDs": ["Messaging", "IDs"],
},
attributes=attributes,
)
assert address.Purpose == "OFFICE"
assert address.Description == "Description"
@@ -77,4 +80,9 @@ class TestEditAddress(test.bootstrap.IFC4):
assert address.PagerNumber == "PagerNumber"
assert address.ElectronicMailAddresses == ("Electronic", "Mail", "Addresses")
assert address.WWWHomePageURL == "WWWHomePageURL"
assert address.MessagingIDs == ("Messaging", "IDs")
if self.file.schema != "IFC2X3":
assert address.MessagingIDs == ("Messaging", "IDs")
class TestEditAddressIFC2X3(test.bootstrap.IFC2X3, TestEditAddress):
pass
@@ -28,11 +28,16 @@ class TestEditOrganisation(test.bootstrap.IFC4):
self.file,
organisation=organisation,
attributes={
"Identification": "Identification",
"Identification" if self.file.schema != "IFC2X3" else "Id": "Identification",
"Name": "Name",
"Description": "Description",
},
)
assert organisation.Identification == "Identification"
# 0 IfcOrganization Identification(>IFC2X3) / Id (IFC2X3)
assert organisation[0] == "Identification"
assert organisation.Name == "Name"
assert organisation.Description == "Description"
class TestEditOrganisationIFC2X3(test.bootstrap.IFC2X3, TestEditOrganisation):
pass
@@ -28,7 +28,7 @@ class TestEditPerson(test.bootstrap.IFC4):
self.file,
person=person,
attributes={
"Identification": "Identification",
"Identification" if self.file.schema != "IFC2X3" else "Id": "Identification",
"FamilyName": "FamilyName",
"GivenName": "GivenName",
"MiddleNames": ["Middle", "Names"],
@@ -36,9 +36,14 @@ class TestEditPerson(test.bootstrap.IFC4):
"SuffixTitles": ["Suffix", "Titles"],
},
)
assert person.Identification == "Identification"
# 0 IfcPerson Identification(>IFC2X3) / Id (IFC2X3)
assert person[0] == "Identification"
assert person.FamilyName == "FamilyName"
assert person.GivenName == "GivenName"
assert person.MiddleNames == ("Middle", "Names")
assert person.PrefixTitles == ("Prefix", "Titles")
assert person.SuffixTitles == ("Suffix", "Titles")
class TestEditPersonIFC2X3(test.bootstrap.IFC2X3, TestEditPerson):
pass
@@ -32,3 +32,7 @@ class TestEditRole(test.bootstrap.IFC4):
assert role.Role == "ARCHITECT"
assert role.UserDefinedRole == "UserDefinedRole"
assert role.Description == "Description"
class TestEditRoleIFC2X3(test.bootstrap.IFC2X3, TestEditRole):
pass
@@ -26,3 +26,7 @@ class TestRemoveActor(test.bootstrap.IFC4):
actor = ifcopenshell.api.run("owner.add_actor", self.file, ifc_class="IfcActor", actor=person)
ifcopenshell.api.run("owner.remove_actor", self.file, actor=actor)
assert len(self.file.by_type("IfcActor")) == 0
class TestRemoveActorIFC2X3(test.bootstrap.IFC2X3, TestRemoveActor):
pass
@@ -41,3 +41,7 @@ class TestRemoveAddress(test.bootstrap.IFC4):
person.Addresses = [address]
ifcopenshell.api.run("owner.remove_address", self.file, address=address)
assert person.Addresses is None
class TestRemoveAddressIFC2X3(test.bootstrap.IFC2X3, TestRemoveAddress):
pass
@@ -21,7 +21,7 @@ import ifcopenshell.api
import ifcopenshell.guid
class TestRemoveOrganisation(test.bootstrap.IFC4):
class TestRemoveOrganisationIFC2X3(test.bootstrap.IFC2X3):
def test_removing_a_organisation(self):
organisation = self.file.createIfcOrganization()
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
@@ -82,26 +82,29 @@ class TestRemoveOrganisation(test.bootstrap.IFC4):
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert document_information.Editors is None
def test_deleting_resource_approval_relationships(self):
organisation = self.file.createIfcOrganization()
self.file.createIfcResourceApprovalRelationship(RelatedResourceObjects=[organisation])
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcResourceApprovalRelationship")) == 0
def test_deleting_resource_constraint_relationships(self):
organisation = self.file.createIfcOrganization()
self.file.createIfcResourceConstraintRelationship(RelatedResourceObjects=[organisation])
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcResourceConstraintRelationship")) == 0
def test_deleting_external_reference_relationships(self):
organisation = self.file.createIfcOrganization()
self.file.createIfcExternalReferenceRelationship(RelatedResourceObjects=[organisation])
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcExternalReferenceRelationship")) == 0
def test_deleting_an_application(self):
organisation = self.file.createIfcOrganization()
self.file.createIfcApplication(ApplicationDeveloper=organisation)
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcApplication")) == 0
class TestRemoveOrganisationIFC4(test.bootstrap.IFC4, TestRemoveOrganisationIFC2X3):
# IfcResourceLevelRelationships were added in IFC4
def test_deleting_resource_approval_relationships(self):
organisation = self.file.create_entity("IfcOrganization")
self.file.create_entity("IfcResourceApprovalRelationship", RelatedResourceObjects=[organisation])
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcResourceApprovalRelationship")) == 0
def test_deleting_resource_constraint_relationships(self):
organisation = self.file.create_entity("IfcOrganization")
self.file.create_entity("IfcResourceConstraintRelationship", RelatedResourceObjects=[organisation])
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcResourceConstraintRelationship")) == 0
def test_deleting_external_reference_relationships(self):
organisation = self.file.create_entity("IfcOrganization")
self.file.create_entity("IfcExternalReferenceRelationship", RelatedResourceObjects=[organisation])
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcExternalReferenceRelationship")) == 0
@@ -16,12 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import pytest
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.guid
class TestRemovePerson(test.bootstrap.IFC4):
class TestRemovePersonIFC2X3(test.bootstrap.IFC2X3):
def test_removing_a_person(self):
person = self.file.createIfcPerson()
ifcopenshell.api.run("owner.remove_person", self.file, person=person)
@@ -61,8 +62,13 @@ class TestRemovePerson(test.bootstrap.IFC4):
def test_ensuring_inventory_should_not_be_left_in_an_invalid_set_cardinality(self):
person = self.file.createIfcPerson()
inventory = self.file.createIfcInventory(ResponsiblePersons=[person])
inventory_id = inventory.id()
ifcopenshell.api.run("owner.remove_person", self.file, person=person)
assert inventory.ResponsiblePersons is None
if self.file.schema != "IFC2X3":
assert inventory.ResponsiblePersons is None
else:
with pytest.raises(RuntimeError):
self.file.by_id(inventory_id)
def test_deleting_person_and_organisations(self):
person = self.file.createIfcPerson()
@@ -82,20 +88,23 @@ class TestRemovePerson(test.bootstrap.IFC4):
ifcopenshell.api.run("owner.remove_person", self.file, person=person)
assert document_information.Editors is None
class TestRemovePersonIFC4(test.bootstrap.IFC4, TestRemovePersonIFC2X3):
# IfcResourceLevelRelationships were added in IFC4
def test_deleting_resource_approval_relationships(self):
person = self.file.createIfcPerson()
self.file.createIfcResourceApprovalRelationship(RelatedResourceObjects=[person])
person = self.file.create_entity("IfcPerson")
self.file.create_entity("IfcResourceApprovalRelationship", RelatedResourceObjects=[person])
ifcopenshell.api.run("owner.remove_person", self.file, person=person)
assert len(self.file.by_type("IfcResourceApprovalRelationship")) == 0
def test_deleting_resource_constraint_relationships(self):
person = self.file.createIfcPerson()
self.file.createIfcResourceConstraintRelationship(RelatedResourceObjects=[person])
person = self.file.create_entity("IfcPerson")
self.file.create_entity("IfcResourceConstraintRelationship", RelatedResourceObjects=[person])
ifcopenshell.api.run("owner.remove_person", self.file, person=person)
assert len(self.file.by_type("IfcResourceConstraintRelationship")) == 0
def test_deleting_external_reference_relationships(self):
person = self.file.createIfcPerson()
self.file.createIfcExternalReferenceRelationship(RelatedResourceObjects=[person])
person = self.file.create_entity("IfcPerson")
self.file.create_entity("IfcExternalReferenceRelationship", RelatedResourceObjects=[person])
ifcopenshell.api.run("owner.remove_person", self.file, person=person)
assert len(self.file.by_type("IfcExternalReferenceRelationship")) == 0
@@ -21,7 +21,7 @@ import ifcopenshell.api
import ifcopenshell.guid
class TestRemovePersonAndOrganisation(test.bootstrap.IFC4):
class TestRemovePersonAndOrganisationIFC2X3(test.bootstrap.IFC2X3):
def test_removing(self):
user = self.file.createIfcPersonAndOrganization()
ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=user)
@@ -39,26 +39,29 @@ class TestRemovePersonAndOrganisation(test.bootstrap.IFC4):
ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=user)
assert document_information.Editors is None
def test_deleting_resource_approval_relationships(self):
user = self.file.createIfcPersonAndOrganization()
self.file.createIfcResourceApprovalRelationship(RelatedResourceObjects=[user])
ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=user)
assert len(self.file.by_type("IfcResourceApprovalRelationship")) == 0
def test_deleting_resource_constraint_relationships(self):
user = self.file.createIfcPersonAndOrganization()
self.file.createIfcResourceConstraintRelationship(RelatedResourceObjects=[user])
ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=user)
assert len(self.file.by_type("IfcResourceConstraintRelationship")) == 0
def test_deleting_external_reference_relationships(self):
user = self.file.createIfcPersonAndOrganization()
self.file.createIfcExternalReferenceRelationship(RelatedResourceObjects=[user])
ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=user)
assert len(self.file.by_type("IfcExternalReferenceRelationship")) == 0
def test_deleting_owner_history(self):
user = self.file.createIfcPersonAndOrganization()
self.file.createIfcOwnerHistory(OwningUser=user)
ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=user)
assert len(self.file.by_type("IfcOwnerHistory")) == 0
class TestRemovePersonAndOrganisationIFC4(test.bootstrap.IFC4, TestRemovePersonAndOrganisationIFC2X3):
# IfcResourceLevelRelationships were added in IFC4
def test_deleting_resource_approval_relationships(self):
user = self.file.create_entity("IfcPersonAndOrganization")
self.file.create_entity("IfcResourceApprovalRelationship", RelatedResourceObjects=[user])
ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=user)
assert len(self.file.by_type("IfcResourceApprovalRelationship")) == 0
def test_deleting_resource_constraint_relationships(self):
user = self.file.create_entity("IfcPersonAndOrganization")
self.file.create_entity("IfcResourceConstraintRelationship", RelatedResourceObjects=[user])
ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=user)
assert len(self.file.by_type("IfcResourceConstraintRelationship")) == 0
def test_deleting_external_reference_relationships(self):
user = self.file.create_entity("IfcPersonAndOrganization")
self.file.create_entity("IfcExternalReferenceRelationship", RelatedResourceObjects=[user])
ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=user)
assert len(self.file.by_type("IfcExternalReferenceRelationship")) == 0
@@ -20,7 +20,7 @@ import test.bootstrap
import ifcopenshell.api
class TestRemoveRole(test.bootstrap.IFC4):
class TestRemoveRoleIFC2X3(test.bootstrap.IFC2X3):
def test_removing_a_role(self):
role = self.file.createIfcActorRole()
ifcopenshell.api.run("owner.remove_role", self.file, role=role)
@@ -47,20 +47,22 @@ class TestRemoveRole(test.bootstrap.IFC4):
ifcopenshell.api.run("owner.remove_role", self.file, role=role)
assert person_and_organisation.Roles is None
class TestRemoveRoleIFC4(test.bootstrap.IFC4, TestRemoveRoleIFC2X3):
def test_deleting_resource_approval_relationships(self):
organisation = self.file.createIfcOrganization()
self.file.createIfcResourceApprovalRelationship(RelatedResourceObjects=[organisation])
organisation = self.file.create_entity("IfcOrganization")
self.file.create_entity("IfcResourceApprovalRelationship", RelatedResourceObjects=[organisation])
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcResourceApprovalRelationship")) == 0
def test_deleting_resource_constraint_relationships(self):
organisation = self.file.createIfcOrganization()
self.file.createIfcResourceConstraintRelationship(RelatedResourceObjects=[organisation])
organisation = self.file.create_entity("IfcOrganization")
self.file.create_entity("IfcResourceConstraintRelationship", RelatedResourceObjects=[organisation])
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcResourceConstraintRelationship")) == 0
def test_deleting_external_reference_relationships(self):
organisation = self.file.createIfcOrganization()
self.file.createIfcExternalReferenceRelationship(RelatedResourceObjects=[organisation])
organisation = self.file.create_entity("IfcOrganization")
self.file.create_entity("IfcExternalReferenceRelationship", RelatedResourceObjects=[organisation])
ifcopenshell.api.run("owner.remove_organisation", self.file, organisation=organisation)
assert len(self.file.by_type("IfcExternalReferenceRelationship")) == 0

Some files were not shown because too many files have changed in this diff Show More