From b8431a8c33b478032af37b0dbea4fca7f2f52366 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 6 May 2023 11:05:13 +1000 Subject: [PATCH] Fix failing tests in preparation for release. --- src/blenderbim/blenderbim/core/drawing.py | 14 ++++---- src/blenderbim/blenderbim/core/tool.py | 1 + src/blenderbim/blenderbim/tool/drawing.py | 3 +- .../test/bim/feature/drawing.feature | 4 +-- src/blenderbim/test/bim/feature/model.feature | 12 +++---- src/blenderbim/test/bim/feature/qto.feature | 7 ++-- src/blenderbim/test/bim/feature/root.feature | 4 +-- src/blenderbim/test/bim/test_feature.py | 33 ++++++++++++------- src/blenderbim/test/core/test_drawing.py | 18 ++++++++++ src/blenderbim/test/tool/test_root.py | 4 +-- .../ifcopenshell/util/resource.py | 22 +++++++------ .../ifcopenshell/util/selector.py | 28 +++++++--------- 12 files changed, 88 insertions(+), 62 deletions(-) diff --git a/src/blenderbim/blenderbim/core/drawing.py b/src/blenderbim/blenderbim/core/drawing.py index 9489a55f8c..957c1fd1df 100644 --- a/src/blenderbim/blenderbim/core/drawing.py +++ b/src/blenderbim/blenderbim/core/drawing.py @@ -312,13 +312,13 @@ def update_drawing_name(ifc, drawing_tool, drawing=None, name=None): if drawing_tool.does_file_exist(resolved_old_location): drawing_tool.move_file(resolved_old_location, ifc.resolve_uri(new_location)) - for reference in drawing_tool.get_references_with_location(old_location): - ifc.run("document.edit_reference", reference=reference, attributes={"Location": new_location}) - sheet = drawing_tool.get_reference_document(reference) - if not sheet: - uri = ifc.resolve_uri(drawing_tool.get_document_uri(sheet, "LAYOUT")) - if drawing_tool.does_file_exist(uri): - drawing_tool.update_embedded_svg_location(uri, old_location, new_location) + for reference in drawing_tool.get_references_with_location(old_location): + ifc.run("document.edit_reference", reference=reference, attributes={"Location": new_location}) + sheet = drawing_tool.get_reference_document(reference) + if sheet: + uri = ifc.resolve_uri(drawing_tool.get_document_uri(sheet, "LAYOUT")) + if drawing_tool.does_file_exist(uri): + drawing_tool.update_embedded_svg_location(uri, old_location, new_location) def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None): diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index e6cb519f11..e24ff4f78e 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -314,6 +314,7 @@ class Drawing: def show_decorations(cls): pass def sync_object_placement(cls, obj): pass def synchronise_ifc_and_text_attributes(cls, obj): pass + def update_embedded_svg_location(cls, uri, old_location, new_location): pass def update_text_size_pset(cls, obj): pass def update_text_value(cls, obj): pass diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py index 96db72c67f..64884e7e58 100644 --- a/src/blenderbim/blenderbim/tool/drawing.py +++ b/src/blenderbim/blenderbim/tool/drawing.py @@ -1457,7 +1457,8 @@ class Drawing(blenderbim.core.tool.Drawing): drawing = tool.Ifc.get_entity(camera) # Running operators is much more efficient in this scenario than looping through each element - bpy.ops.object.hide_view_clear() + if not bpy.app.background: + bpy.ops.object.hide_view_clear() filtered_elements = cls.get_drawing_elements(drawing) | cls.get_drawing_spaces(drawing) hidden_objs = [o for o in bpy.context.visible_objects if tool.Ifc.get_entity(o) not in filtered_elements] diff --git a/src/blenderbim/test/bim/feature/drawing.feature b/src/blenderbim/test/bim/feature/drawing.feature index 09fde31b26..2adfac3d39 100644 --- a/src/blenderbim/test/bim/feature/drawing.feature +++ b/src/blenderbim/test/bim/feature/drawing.feature @@ -23,7 +23,7 @@ Scenario: Create drawing And I press "bim.add_drawing" And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()" And I set "scene.DocProperties.active_drawing_index" to "0" - And I press "bim.activate_view(drawing={drawing})" + And I press "bim.activate_drawing(drawing={drawing})" When I press "bim.create_drawing" Then nothing happens @@ -41,7 +41,7 @@ Scenario: Create drawing after deleting a duplicated object And I press "bim.add_drawing" And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()" And I set "scene.DocProperties.active_drawing_index" to "0" - And I press "bim.activate_view(drawing={drawing})" + And I press "bim.activate_drawing(drawing={drawing})" And I press "bim.create_drawing" And the object "IfcWall/Cube" is selected And I press "object.delete(use_global=False)" diff --git a/src/blenderbim/test/bim/feature/model.feature b/src/blenderbim/test/bim/feature/model.feature index 7544f8762a..12ce9a041d 100644 --- a/src/blenderbim/test/bim/feature/model.feature +++ b/src/blenderbim/test/bim/feature/model.feature @@ -194,8 +194,8 @@ Scenario: Change the height of a wall And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}" And I press "bim.hotkey(hotkey='S_A')" And the object "IfcWall/Wall" is selected - And I set "scene.BIMModelProperties.extrusion_depth" to "2000.0" - When I press "bim.change_extrusion_depth(depth=2000.0)" + And I set "scene.BIMModelProperties.extrusion_depth" to "2.0" + When I press "bim.change_extrusion_depth(depth=2.0)" Then the object "IfcWall/Wall" dimensions are "1,0.1,2" Scenario: Change the length of a wall @@ -206,8 +206,8 @@ Scenario: Change the length of a wall And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}" And I press "bim.hotkey(hotkey='S_A')" And the object "IfcWall/Wall" is selected - And I set "scene.BIMModelProperties.length" to "2000.0" - When I press "bim.change_layer_length(length=2000.0)" + And I set "scene.BIMModelProperties.length" to "2.0" + When I press "bim.change_layer_length(length=2.0)" Then the object "IfcWall/Wall" dimensions are "2,0.1,3" Scenario: Flip a wall @@ -438,8 +438,8 @@ Scenario: Change the length of a beam And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}" And I press "bim.hotkey(hotkey='S_A')" And the object "IfcBeam/Beam" is selected - And I set "scene.BIMModelProperties.extrusion_depth" to "2000.0" - When I press "bim.change_profile_depth(depth=2000.0)" + And I set "scene.BIMModelProperties.extrusion_depth" to "2.0" + When I press "bim.change_profile_depth(depth=2.0)" Then the object "IfcBeam/Beam" dimensions are "0.1,0.2,2" Scenario: Rotate a beam by 90 degrees diff --git a/src/blenderbim/test/bim/feature/qto.feature b/src/blenderbim/test/bim/feature/qto.feature index 42fbff4474..85b4d59cc7 100644 --- a/src/blenderbim/test/bim/feature/qto.feature +++ b/src/blenderbim/test/bim/feature/qto.feature @@ -47,7 +47,7 @@ Scenario: Execute qto method - side formwork areas And I press "bim.execute_qto_method" Then "scene.BIMQtoProperties.qto_result" is "16.0" - Scenario: Execute assign_objects_base_qto + Scenario: Assign objects base qto Given an empty IFC project And I add a cube And the object "Cube" is selected @@ -55,7 +55,7 @@ Scenario: Execute qto method - side formwork areas When I press "bim.assign_objects_base_qto" Then "active_object.PsetProperties.qto_name" is "Qto_WallBaseQuantities" -Scenario: Execute calculate_all_quantities +Scenario: Calculate all quantities Given an empty IFC project And I add a cube And the object "Cube" is selected @@ -65,5 +65,4 @@ Scenario: Execute calculate_all_quantities And I press "bim.enable_pset_editing(pset_id={qset_id}, obj='IfcWall/Cube', obj_type='Object')" And I press "bim.disable_pset_editing(obj='IfcWall/Cube', obj_type='Object')" Then "active_object.PsetProperties.qto_name" is "Qto_WallBaseQuantities" - Then "active_object.PsetProperties.properties['Length'].metadata.float_value" is "2.0" - + Then "active_object.PsetProperties.properties['Length'].metadata.float_value" is "2000.0" diff --git a/src/blenderbim/test/bim/feature/root.feature b/src/blenderbim/test/bim/feature/root.feature index 16e7adb964..28b9e71726 100644 --- a/src/blenderbim/test/bim/feature/root.feature +++ b/src/blenderbim/test/bim/feature/root.feature @@ -25,8 +25,8 @@ Scenario: Unlink object And I press "bim.add_material" When I press "bim.unlink_object(obj='IfcWall/Cube')" Then the object "Cube" is not an IFC element - And the material "Material" is not an IFC material - And the material "Material" is not an IFC style + And the material "Material" is an IFC style + And the material "Material.001" is not an IFC style Scenario: Copy class Given an empty IFC project diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index baae5eb5e5..4100656700 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -431,8 +431,9 @@ def the_object_name_is_an_ifc_class(name, ifc_class): @then(parsers.parse('the object "{name}" is not an IFC element')) def the_object_name_is_not_an_ifc_element(name): - id = the_object_name_exists(name).BIMObjectProperties.ifc_definition_id - assert id == 0, f"The ID is {id}" + obj = the_object_name_exists(name) + ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id + assert ifc_definition_id == 0, f"The object {obj} has an ID of {ifc_definition_id}" @then(parsers.parse('the object "{name}" has no data')) @@ -447,21 +448,31 @@ def the_object_name_is_not_an_ifc_element(name): @then(parsers.parse('the material "{name}" is an IFC material')) -def the_material_name_is_not_an_ifc_material(name): - id = the_material_name_exists(name).BIMObjectProperties.ifc_definition_id - assert id != 0, f"The ID is {id}" +def the_material_name_is_an_ifc_material(name): + obj = the_material_name_exists(name) + ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id + assert ifc_definition_id != 0, f"The material {obj} has no ID: {ifc_definition_id}" @then(parsers.parse('the material "{name}" is not an IFC material')) def the_material_name_is_not_an_ifc_material(name): - id = the_material_name_exists(name).BIMObjectProperties.ifc_definition_id - assert id == 0, f"The ID is {id}" + obj = the_material_name_exists(name) + ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id + assert ifc_definition_id == 0, f"The material {obj} has an ID of {ifc_definition_id}" + + +@then(parsers.parse('the material "{name}" is an IFC style')) +def the_material_name_is_an_ifc_style(name): + obj = the_material_name_exists(name) + ifc_definition_id = obj.BIMMaterialProperties.ifc_style_id + assert ifc_definition_id != 0, f"The material {obj} has a style ID of {ifc_definition_id}" @then(parsers.parse('the material "{name}" is not an IFC style')) -def the_material_name_is_not_an_ifc_material(name): - id = the_material_name_exists(name).BIMMaterialProperties.ifc_style_id - assert id == 0, f"The ID is {id}" +def the_material_name_is_not_an_ifc_style(name): + obj = the_material_name_exists(name) + ifc_definition_id = obj.BIMMaterialProperties.ifc_style_id + assert ifc_definition_id == 0, f"The material {obj} has a style ID of {ifc_definition_id}" @then(parsers.parse('the material "{name}" colour is "{colour}"')) @@ -636,7 +647,7 @@ def the_object_name_dimensions_are_dimensions(name, dimensions): actual_dimensions = list(the_object_name_exists(name).dimensions) expected_dimensions = [float(co) for co in dimensions.split(",")] for i, number in enumerate(actual_dimensions): - assert is_x(number, expected_dimensions[i]) + assert is_x(number, expected_dimensions[i]), f"Expected {actual_dimensions[i]} but got {number}" @then(parsers.parse('the object "{name}" top right corner is at "{location}"')) diff --git a/src/blenderbim/test/core/test_drawing.py b/src/blenderbim/test/core/test_drawing.py index 2adc641d08..8c0dd74cba 100644 --- a/src/blenderbim/test/core/test_drawing.py +++ b/src/blenderbim/test/core/test_drawing.py @@ -391,6 +391,7 @@ class TestUpdateDrawingName: ifc.run("document.edit_information", information="information", attributes={"Name": "name"}).should_be_called() drawing.get_reference_location("reference").should_be_called().will_return("old_location") drawing.get_default_drawing_path("name").should_be_called().will_return("new_location") + ifc.run( "document.edit_reference", reference="reference", attributes={"Location": "new_location"} ).should_be_called() @@ -399,6 +400,23 @@ class TestUpdateDrawingName: ifc.resolve_uri("new_location").should_be_called().will_return("new_uri") drawing.move_file("old_uri", "new_uri").should_be_called() + drawing.get_references_with_location("old_location").should_be_called().will_return( + ["reference_with_old_location"] + ) + ifc.run( + "document.edit_reference", reference="reference_with_old_location", attributes={"Location": "new_location"} + ).should_be_called() + drawing.get_reference_document("reference_with_old_location").should_be_called().will_return( + "sheet_with_old_location" + ) + + drawing.get_document_uri("sheet_with_old_location", "LAYOUT").should_be_called().will_return( + "relative_layout_uri" + ) + ifc.resolve_uri("relative_layout_uri").should_be_called().will_return("absolute_layout_uri") + drawing.does_file_exist("absolute_layout_uri").should_be_called().will_return(True) + drawing.update_embedded_svg_location("absolute_layout_uri", "old_location", "new_location").should_be_called() + subject.update_drawing_name(ifc, drawing, drawing="drawing", name="name") diff --git a/src/blenderbim/test/tool/test_root.py b/src/blenderbim/test/tool/test_root.py index 15ddf8a115..5f9ac6c063 100644 --- a/src/blenderbim/test/tool/test_root.py +++ b/src/blenderbim/test/tool/test_root.py @@ -135,8 +135,8 @@ class TestGetRepresentationContext(NewFile): class TestIsElementA(NewFile): def test_run(self): ifc = ifcopenshell.file() - assert subject.is_a(ifc.createIfcWall(), "IfcSlab") is False - assert subject.is_a(ifc.createIfcOpeningElement(), "IfcOpeningElement") is True + assert subject.is_element_a(ifc.createIfcWall(), "IfcSlab") is False + assert subject.is_element_a(ifc.createIfcOpeningElement(), "IfcOpeningElement") is True class TestLinkObjectData(NewFile): diff --git a/src/ifcopenshell-python/ifcopenshell/util/resource.py b/src/ifcopenshell-python/ifcopenshell/util/resource.py index d3d118c5f5..016a653209 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/resource.py +++ b/src/ifcopenshell-python/ifcopenshell/util/resource.py @@ -21,36 +21,36 @@ import ifcopenshell.util.date def get_productivity(resource, should_inherit=True): - productivity = ifcopenshell.util.element.get_psets( - resource - ).get("EPset_Productivity", None) + productivity = ifcopenshell.util.element.get_psets(resource).get("EPset_Productivity", None) if should_inherit and not productivity: # Proposal for Schema - If instance doesn't have any productivity, inherit it's parent's productivity if not resource.Nests: return None else: parent_resource = resource.Nests[0].RelatingObject - productivity = ifcopenshell.util.element.get_psets( - parent_resource - ).get("EPset_Productivity", None) + productivity = ifcopenshell.util.element.get_psets(parent_resource).get("EPset_Productivity", None) return productivity + def get_unit_consumed(productivity): duration = productivity.get("BaseQuantityConsumed", None) if not duration: return return ifcopenshell.util.date.ifc2datetime(duration) + def get_quantity_produced(productivity): if not productivity: return 0 return productivity.get("BaseQuantityProducedValue", 0) + def get_quantity_produced_name(productivity): if not productivity: return "" return productivity.get("BaseQuantityProducedName", "") + def get_total_quantity_produced(resource, quantity_name_in_process): def get_product_quantity(product, quantity_name): psets = ifcopenshell.util.element.get_psets(product) @@ -65,9 +65,10 @@ def get_total_quantity_produced(resource, quantity_name_in_process): total = len(products) else: for product in products: - total += get_product_quantity(product, quantity_name_in_process) + total += get_product_quantity(product, quantity_name_in_process) or 0 return total + def get_parametric_resource_products(resource): products = [] for rel in resource.HasAssignments or []: @@ -79,6 +80,7 @@ def get_parametric_resource_products(resource): products.append(rel2.RelatingProduct) return products + def get_resource_required_work(resource): productivity = get_productivity(resource) if productivity: @@ -92,11 +94,11 @@ def get_resource_required_work(resource): if "T" in productivity.get("BaseQuantityConsumed", ""): seconds = (time_consumed.days * 24 * 60 * 60) + time_consumed.seconds productivity_ratio = seconds / quantity_produced - required_work = total_quantity_to_produce * productivity_ratio + required_work = total_quantity_to_produce * productivity_ratio iso_string = f"PT{required_work / 60 / 60}H" else: days = time_consumed.days + (time_consumed.seconds / (24 * 60 * 60)) - productivity_ratio = days / quantity_produced + productivity_ratio = days / quantity_produced required_work = total_quantity_to_produce * productivity_ratio iso_string = f"P{required_work}D" - return iso_string \ No newline at end of file + return iso_string diff --git a/src/ifcopenshell-python/ifcopenshell/util/selector.py b/src/ifcopenshell-python/ifcopenshell/util/selector.py index 216b807799..71705c788a 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/selector.py +++ b/src/ifcopenshell-python/ifcopenshell/util/selector.py @@ -159,9 +159,7 @@ class Selector: if not inverse_relationship: return results - return cls.parse_inverse_relationship( - results, inverse_relationship.children[0].data - ) + return cls.parse_inverse_relationship(results, inverse_relationship.children[0].data) @classmethod def parse_inverse_relationship(cls, elements, inverse_relationship): @@ -193,13 +191,8 @@ class Selector: if cls.elements is None: elements = cls.file.by_type(class_selector.children[0]) else: - elements = [ - e for e in cls.elements if e.is_a(class_selector.children[0]) - ] - if ( - len(class_selector.children) > 1 - and class_selector.children[1].data == "filter" - ): + elements = [e for e in cls.elements if e.is_a(class_selector.children[0])] + if len(class_selector.children) > 1 and class_selector.children[1].data == "filter": return cls.filter_elements(elements, class_selector.children[1]) return elements @@ -227,9 +220,7 @@ class Selector: element_value = cls.get_element_value(element, filter_query["keys"], is_regex=filter_query["is_regex"]) if element_value is None and value is not None and "not" not in comparison: continue - if comparison and cls.filter_element( - element, element_value, comparison, value - ): + if comparison and cls.filter_element(element, element_value, comparison, value): results.append(element) elif not comparison and element_value: results.append(element) @@ -243,9 +234,9 @@ class Selector: keys = [keys] elif keys.data == "keys_regex": is_regex = True - keys = [k[1:-1].replace("\\\"", '"') for k in keys.children] + keys = [k[1:-1].replace('\\"', '"') for k in keys.children] elif keys.data == "keys_quoted": - keys = [k[1:-1].replace("\\\"", '"') for k in keys.children] + keys = [k[1:-1].replace('\\"', '"') for k in keys.children] elif keys.data == "keys_simple": keys = keys.children return {"keys": keys, "is_regex": is_regex} @@ -273,6 +264,9 @@ class Selector: elif key == "class": value = value.is_a() elif isinstance(value, ifcopenshell.entity_instance): + if key == "Name" and value.is_a("IfcMaterialLayerSet"): + key = "LayerSetName" # This oddity in the IFC spec is annoying so we account for it. + attribute = value.get_info().get(key, None) if attribute is not None: @@ -290,7 +284,7 @@ class Selector: result = ifcopenshell.util.element.get_pset(value, key) value = result - elif isinstance(value, dict): # Such as from the result of a prior get_pset + elif isinstance(value, dict): # Such as from the result of a prior get_pset if is_regex: results = [] for prop_name, prop_value in value.items(): @@ -302,7 +296,7 @@ class Selector: value = results else: value = value.get(key, None) - elif isinstance(value, (list, tuple)): # If we use regex + elif isinstance(value, (list, tuple)): # If we use regex if key.isnumeric(): try: value = value[int(key)]