Fix failing tests in preparation for release.

This commit is contained in:
Dion Moult
2023-05-06 11:05:13 +10:00
parent 34d947c51f
commit 1fb6dac6fb
12 changed files with 88 additions and 62 deletions
+7 -7
View File
@@ -312,13 +312,13 @@ def update_drawing_name(ifc, drawing_tool, drawing=None, name=None):
if drawing_tool.does_file_exist(resolved_old_location): if drawing_tool.does_file_exist(resolved_old_location):
drawing_tool.move_file(resolved_old_location, ifc.resolve_uri(new_location)) drawing_tool.move_file(resolved_old_location, ifc.resolve_uri(new_location))
for reference in drawing_tool.get_references_with_location(old_location): for reference in drawing_tool.get_references_with_location(old_location):
ifc.run("document.edit_reference", reference=reference, attributes={"Location": new_location}) ifc.run("document.edit_reference", reference=reference, attributes={"Location": new_location})
sheet = drawing_tool.get_reference_document(reference) sheet = drawing_tool.get_reference_document(reference)
if not sheet: if sheet:
uri = ifc.resolve_uri(drawing_tool.get_document_uri(sheet, "LAYOUT")) uri = ifc.resolve_uri(drawing_tool.get_document_uri(sheet, "LAYOUT"))
if drawing_tool.does_file_exist(uri): if drawing_tool.does_file_exist(uri):
drawing_tool.update_embedded_svg_location(uri, old_location, new_location) drawing_tool.update_embedded_svg_location(uri, old_location, new_location)
def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None): def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None):
+1
View File
@@ -314,6 +314,7 @@ class Drawing:
def show_decorations(cls): pass def show_decorations(cls): pass
def sync_object_placement(cls, obj): pass def sync_object_placement(cls, obj): pass
def synchronise_ifc_and_text_attributes(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_size_pset(cls, obj): pass
def update_text_value(cls, obj): pass def update_text_value(cls, obj): pass
+2 -1
View File
@@ -1457,7 +1457,8 @@ class Drawing(blenderbim.core.tool.Drawing):
drawing = tool.Ifc.get_entity(camera) drawing = tool.Ifc.get_entity(camera)
# Running operators is much more efficient in this scenario than looping through each element # 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) 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] hidden_objs = [o for o in bpy.context.visible_objects if tool.Ifc.get_entity(o) not in filtered_elements]
@@ -23,7 +23,7 @@ Scenario: Create drawing
And I press "bim.add_drawing" And I press "bim.add_drawing"
And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()" 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 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" When I press "bim.create_drawing"
Then nothing happens Then nothing happens
@@ -41,7 +41,7 @@ Scenario: Create drawing after deleting a duplicated object
And I press "bim.add_drawing" And I press "bim.add_drawing"
And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()" 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 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 I press "bim.create_drawing"
And the object "IfcWall/Cube" is selected And the object "IfcWall/Cube" is selected
And I press "object.delete(use_global=False)" And I press "object.delete(use_global=False)"
@@ -194,8 +194,8 @@ Scenario: Change the height of a wall
And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}" And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}"
And I press "bim.hotkey(hotkey='S_A')" And I press "bim.hotkey(hotkey='S_A')"
And the object "IfcWall/Wall" is selected And the object "IfcWall/Wall" is selected
And I set "scene.BIMModelProperties.extrusion_depth" to "2000.0" And I set "scene.BIMModelProperties.extrusion_depth" to "2.0"
When I press "bim.change_extrusion_depth(depth=2000.0)" When I press "bim.change_extrusion_depth(depth=2.0)"
Then the object "IfcWall/Wall" dimensions are "1,0.1,2" Then the object "IfcWall/Wall" dimensions are "1,0.1,2"
Scenario: Change the length of a wall 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 set "scene.BIMModelProperties.relating_type_id" to "{element_type}"
And I press "bim.hotkey(hotkey='S_A')" And I press "bim.hotkey(hotkey='S_A')"
And the object "IfcWall/Wall" is selected And the object "IfcWall/Wall" is selected
And I set "scene.BIMModelProperties.length" to "2000.0" And I set "scene.BIMModelProperties.length" to "2.0"
When I press "bim.change_layer_length(length=2000.0)" When I press "bim.change_layer_length(length=2.0)"
Then the object "IfcWall/Wall" dimensions are "2,0.1,3" Then the object "IfcWall/Wall" dimensions are "2,0.1,3"
Scenario: Flip a wall 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 set "scene.BIMModelProperties.relating_type_id" to "{element_type}"
And I press "bim.hotkey(hotkey='S_A')" And I press "bim.hotkey(hotkey='S_A')"
And the object "IfcBeam/Beam" is selected And the object "IfcBeam/Beam" is selected
And I set "scene.BIMModelProperties.extrusion_depth" to "2000.0" And I set "scene.BIMModelProperties.extrusion_depth" to "2.0"
When I press "bim.change_profile_depth(depth=2000.0)" When I press "bim.change_profile_depth(depth=2.0)"
Then the object "IfcBeam/Beam" dimensions are "0.1,0.2,2" Then the object "IfcBeam/Beam" dimensions are "0.1,0.2,2"
Scenario: Rotate a beam by 90 degrees Scenario: Rotate a beam by 90 degrees
+3 -4
View File
@@ -47,7 +47,7 @@ Scenario: Execute qto method - side formwork areas
And I press "bim.execute_qto_method" And I press "bim.execute_qto_method"
Then "scene.BIMQtoProperties.qto_result" is "16.0" Then "scene.BIMQtoProperties.qto_result" is "16.0"
Scenario: Execute assign_objects_base_qto Scenario: Assign objects base qto
Given an empty IFC project Given an empty IFC project
And I add a cube And I add a cube
And the object "Cube" is selected 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" When I press "bim.assign_objects_base_qto"
Then "active_object.PsetProperties.qto_name" is "Qto_WallBaseQuantities" Then "active_object.PsetProperties.qto_name" is "Qto_WallBaseQuantities"
Scenario: Execute calculate_all_quantities Scenario: Calculate all quantities
Given an empty IFC project Given an empty IFC project
And I add a cube And I add a cube
And the object "Cube" is selected 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.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')" 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.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"
+2 -2
View File
@@ -25,8 +25,8 @@ Scenario: Unlink object
And I press "bim.add_material" And I press "bim.add_material"
When I press "bim.unlink_object(obj='IfcWall/Cube')" When I press "bim.unlink_object(obj='IfcWall/Cube')"
Then the object "Cube" is not an IFC element Then the object "Cube" is not an IFC element
And the material "Material" is not an IFC material And the material "Material" is an IFC style
And the material "Material" is not an IFC style And the material "Material.001" is not an IFC style
Scenario: Copy class Scenario: Copy class
Given an empty IFC project Given an empty IFC project
+22 -11
View File
@@ -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')) @then(parsers.parse('the object "{name}" is not an IFC element'))
def the_object_name_is_not_an_ifc_element(name): def the_object_name_is_not_an_ifc_element(name):
id = the_object_name_exists(name).BIMObjectProperties.ifc_definition_id obj = the_object_name_exists(name)
assert id == 0, f"The ID is {id}" 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')) @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')) @then(parsers.parse('the material "{name}" is an IFC material'))
def the_material_name_is_not_an_ifc_material(name): def the_material_name_is_an_ifc_material(name):
id = the_material_name_exists(name).BIMObjectProperties.ifc_definition_id obj = the_material_name_exists(name)
assert id != 0, f"The ID is {id}" 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')) @then(parsers.parse('the material "{name}" is not an IFC material'))
def the_material_name_is_not_an_ifc_material(name): def the_material_name_is_not_an_ifc_material(name):
id = the_material_name_exists(name).BIMObjectProperties.ifc_definition_id obj = the_material_name_exists(name)
assert id == 0, f"The ID is {id}" 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')) @then(parsers.parse('the material "{name}" is not an IFC style'))
def the_material_name_is_not_an_ifc_material(name): def the_material_name_is_not_an_ifc_style(name):
id = the_material_name_exists(name).BIMMaterialProperties.ifc_style_id obj = the_material_name_exists(name)
assert id == 0, f"The ID is {id}" 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}"')) @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) actual_dimensions = list(the_object_name_exists(name).dimensions)
expected_dimensions = [float(co) for co in dimensions.split(",")] expected_dimensions = [float(co) for co in dimensions.split(",")]
for i, number in enumerate(actual_dimensions): 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}"')) @then(parsers.parse('the object "{name}" top right corner is at "{location}"'))
+18
View File
@@ -391,6 +391,7 @@ class TestUpdateDrawingName:
ifc.run("document.edit_information", information="information", attributes={"Name": "name"}).should_be_called() 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_reference_location("reference").should_be_called().will_return("old_location")
drawing.get_default_drawing_path("name").should_be_called().will_return("new_location") drawing.get_default_drawing_path("name").should_be_called().will_return("new_location")
ifc.run( ifc.run(
"document.edit_reference", reference="reference", attributes={"Location": "new_location"} "document.edit_reference", reference="reference", attributes={"Location": "new_location"}
).should_be_called() ).should_be_called()
@@ -399,6 +400,23 @@ class TestUpdateDrawingName:
ifc.resolve_uri("new_location").should_be_called().will_return("new_uri") ifc.resolve_uri("new_location").should_be_called().will_return("new_uri")
drawing.move_file("old_uri", "new_uri").should_be_called() 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") subject.update_drawing_name(ifc, drawing, drawing="drawing", name="name")
+2 -2
View File
@@ -135,8 +135,8 @@ class TestGetRepresentationContext(NewFile):
class TestIsElementA(NewFile): class TestIsElementA(NewFile):
def test_run(self): def test_run(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
assert subject.is_a(ifc.createIfcWall(), "IfcSlab") is False assert subject.is_element_a(ifc.createIfcWall(), "IfcSlab") is False
assert subject.is_a(ifc.createIfcOpeningElement(), "IfcOpeningElement") is True assert subject.is_element_a(ifc.createIfcOpeningElement(), "IfcOpeningElement") is True
class TestLinkObjectData(NewFile): class TestLinkObjectData(NewFile):
@@ -21,36 +21,36 @@ import ifcopenshell.util.date
def get_productivity(resource, should_inherit=True): def get_productivity(resource, should_inherit=True):
productivity = ifcopenshell.util.element.get_psets( productivity = ifcopenshell.util.element.get_psets(resource).get("EPset_Productivity", None)
resource
).get("EPset_Productivity", None)
if should_inherit and not productivity: if should_inherit and not productivity:
# Proposal for Schema - If instance doesn't have any productivity, inherit it's parent's productivity # Proposal for Schema - If instance doesn't have any productivity, inherit it's parent's productivity
if not resource.Nests: if not resource.Nests:
return None return None
else: else:
parent_resource = resource.Nests[0].RelatingObject parent_resource = resource.Nests[0].RelatingObject
productivity = ifcopenshell.util.element.get_psets( productivity = ifcopenshell.util.element.get_psets(parent_resource).get("EPset_Productivity", None)
parent_resource
).get("EPset_Productivity", None)
return productivity return productivity
def get_unit_consumed(productivity): def get_unit_consumed(productivity):
duration = productivity.get("BaseQuantityConsumed", None) duration = productivity.get("BaseQuantityConsumed", None)
if not duration: if not duration:
return return
return ifcopenshell.util.date.ifc2datetime(duration) return ifcopenshell.util.date.ifc2datetime(duration)
def get_quantity_produced(productivity): def get_quantity_produced(productivity):
if not productivity: if not productivity:
return 0 return 0
return productivity.get("BaseQuantityProducedValue", 0) return productivity.get("BaseQuantityProducedValue", 0)
def get_quantity_produced_name(productivity): def get_quantity_produced_name(productivity):
if not productivity: if not productivity:
return "" return ""
return productivity.get("BaseQuantityProducedName", "") return productivity.get("BaseQuantityProducedName", "")
def get_total_quantity_produced(resource, quantity_name_in_process): def get_total_quantity_produced(resource, quantity_name_in_process):
def get_product_quantity(product, quantity_name): def get_product_quantity(product, quantity_name):
psets = ifcopenshell.util.element.get_psets(product) psets = ifcopenshell.util.element.get_psets(product)
@@ -65,9 +65,10 @@ def get_total_quantity_produced(resource, quantity_name_in_process):
total = len(products) total = len(products)
else: else:
for product in products: 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 return total
def get_parametric_resource_products(resource): def get_parametric_resource_products(resource):
products = [] products = []
for rel in resource.HasAssignments or []: for rel in resource.HasAssignments or []:
@@ -79,6 +80,7 @@ def get_parametric_resource_products(resource):
products.append(rel2.RelatingProduct) products.append(rel2.RelatingProduct)
return products return products
def get_resource_required_work(resource): def get_resource_required_work(resource):
productivity = get_productivity(resource) productivity = get_productivity(resource)
if productivity: if productivity:
@@ -92,11 +94,11 @@ def get_resource_required_work(resource):
if "T" in productivity.get("BaseQuantityConsumed", ""): if "T" in productivity.get("BaseQuantityConsumed", ""):
seconds = (time_consumed.days * 24 * 60 * 60) + time_consumed.seconds seconds = (time_consumed.days * 24 * 60 * 60) + time_consumed.seconds
productivity_ratio = seconds / quantity_produced 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" iso_string = f"PT{required_work / 60 / 60}H"
else: else:
days = time_consumed.days + (time_consumed.seconds / (24 * 60 * 60)) 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 required_work = total_quantity_to_produce * productivity_ratio
iso_string = f"P{required_work}D" iso_string = f"P{required_work}D"
return iso_string return iso_string
@@ -159,9 +159,7 @@ class Selector:
if not inverse_relationship: if not inverse_relationship:
return results return results
return cls.parse_inverse_relationship( return cls.parse_inverse_relationship(results, inverse_relationship.children[0].data)
results, inverse_relationship.children[0].data
)
@classmethod @classmethod
def parse_inverse_relationship(cls, elements, inverse_relationship): def parse_inverse_relationship(cls, elements, inverse_relationship):
@@ -193,13 +191,8 @@ class Selector:
if cls.elements is None: if cls.elements is None:
elements = cls.file.by_type(class_selector.children[0]) elements = cls.file.by_type(class_selector.children[0])
else: else:
elements = [ elements = [e for e in cls.elements if e.is_a(class_selector.children[0])]
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":
]
if (
len(class_selector.children) > 1
and class_selector.children[1].data == "filter"
):
return cls.filter_elements(elements, class_selector.children[1]) return cls.filter_elements(elements, class_selector.children[1])
return elements return elements
@@ -227,9 +220,7 @@ class Selector:
element_value = cls.get_element_value(element, filter_query["keys"], is_regex=filter_query["is_regex"]) 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: if element_value is None and value is not None and "not" not in comparison:
continue continue
if comparison and cls.filter_element( if comparison and cls.filter_element(element, element_value, comparison, value):
element, element_value, comparison, value
):
results.append(element) results.append(element)
elif not comparison and element_value: elif not comparison and element_value:
results.append(element) results.append(element)
@@ -243,9 +234,9 @@ class Selector:
keys = [keys] keys = [keys]
elif keys.data == "keys_regex": elif keys.data == "keys_regex":
is_regex = True 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": 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": elif keys.data == "keys_simple":
keys = keys.children keys = keys.children
return {"keys": keys, "is_regex": is_regex} return {"keys": keys, "is_regex": is_regex}
@@ -273,6 +264,9 @@ class Selector:
elif key == "class": elif key == "class":
value = value.is_a() value = value.is_a()
elif isinstance(value, ifcopenshell.entity_instance): 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) attribute = value.get_info().get(key, None)
if attribute is not None: if attribute is not None:
@@ -290,7 +284,7 @@ class Selector:
result = ifcopenshell.util.element.get_pset(value, key) result = ifcopenshell.util.element.get_pset(value, key)
value = result 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: if is_regex:
results = [] results = []
for prop_name, prop_value in value.items(): for prop_name, prop_value in value.items():
@@ -302,7 +296,7 @@ class Selector:
value = results value = results
else: else:
value = value.get(key, None) 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(): if key.isnumeric():
try: try:
value = value[int(key)] value = value[int(key)]