diff --git a/src/ifc5d/ifc5d/ifc5Dspreadsheet.py b/src/ifc5d/ifc5d/ifc5Dspreadsheet.py index a0f12e2535..a57e52426c 100644 --- a/src/ifc5d/ifc5d/ifc5Dspreadsheet.py +++ b/src/ifc5d/ifc5d/ifc5Dspreadsheet.py @@ -269,15 +269,18 @@ class IfcDataGetter: if obj.is_a("IfcElement"): prefix += (obj.Name or "") + " - " name = prefix + (quantity.Name or "") + # Formula is an optional IfcLabel on IfcQuantity* in IFC4+; absent in + # IFC2X3, hence the schema-safe getattr. + formula = getattr(quantity, "Formula", None) or "" if quantity.is_a("IfcPhysicalSimpleQuantity"): value = quantity[3] try: value = float(value) if value is not None else 0.0 except (TypeError, ValueError): value = 0.0 - result.append([name, value]) + result.append([name, value, formula]) else: - result.append([name + " ERROR: Only IfcPhysicalSimpleQuantity is supported", 0.0]) + result.append([name + " ERROR: Only IfcPhysicalSimpleQuantity is supported", 0.0, formula]) return json.dumps(result, ensure_ascii=False) diff --git a/src/ifc5d/test/test_csv2ifc.py b/src/ifc5d/test/test_csv2ifc.py index a630bb44ac..01b4a85ee6 100644 --- a/src/ifc5d/test/test_csv2ifc.py +++ b/src/ifc5d/test/test_csv2ifc.py @@ -130,7 +130,7 @@ class TestSerialiseCostQuantities: result = ifc5d.ifc5Dspreadsheet.IfcDataGetter.serialise_cost_quantities(ifc_file, cost_item) - assert json.loads(result) == [[name, 12.5]] + assert json.loads(result) == [[name, 12.5, ""]] def test_unset_name_does_not_crash(self): ifc_file = ifcopenshell.file() @@ -140,4 +140,23 @@ class TestSerialiseCostQuantities: result = ifc5d.ifc5Dspreadsheet.IfcDataGetter.serialise_cost_quantities(ifc_file, cost_item) - assert json.loads(result) == [["", 3.0]] + assert json.loads(result) == [["", 3.0, ""]] + + def test_formula_is_included_when_present(self): + ifc_file = ifcopenshell.file() + quantity = ifc_file.create_entity("IfcQuantityArea", Name="Area", AreaValue=12.5, Formula="Length * Width") + cost_item = ifc_file.create_entity("IfcCostItem", CostQuantities=[quantity]) + + result = ifc5d.ifc5Dspreadsheet.IfcDataGetter.serialise_cost_quantities(ifc_file, cost_item) + + assert json.loads(result) == [["Area", 12.5, "Length * Width"]] + + def test_quantity_without_formula_attribute_does_not_crash(self): + # IfcPhysicalComplexQuantity has no Formula attribute and is unsupported. + ifc_file = ifcopenshell.file() + quantity = ifc_file.create_entity("IfcPhysicalComplexQuantity", Name="Complex", Discrimination="layer") + cost_item = ifc_file.create_entity("IfcCostItem", CostQuantities=[quantity]) + + result = ifc5d.ifc5Dspreadsheet.IfcDataGetter.serialise_cost_quantities(ifc_file, cost_item) + + assert json.loads(result) == [["Complex ERROR: Only IfcPhysicalSimpleQuantity is supported", 0.0, ""]]