diff --git a/src/ifc5d/ifc5d/ifc5Dspreadsheet.py b/src/ifc5d/ifc5d/ifc5Dspreadsheet.py index 5aab765c26..a0f12e2535 100644 --- a/src/ifc5d/ifc5d/ifc5Dspreadsheet.py +++ b/src/ifc5d/ifc5d/ifc5Dspreadsheet.py @@ -21,6 +21,7 @@ from __future__ import annotations import argparse import datetime +import json import logging import os import time @@ -256,26 +257,28 @@ class IfcDataGetter: return "" if cost_item.CostQuantities is None: return "" - string = "[" + result = [] for quantity in cost_item.CostQuantities: - string += '["' + prefix = "" for rel in file.get_inverse(quantity): if rel.is_a("IfcPropertySet") or rel.is_a("IfcElementQuantity"): - prop_set = rel # Find elements that have this property set - for prop_rel in file.get_inverse(prop_set): + for prop_rel in file.get_inverse(rel): if prop_rel.is_a("IfcRelDefinesByProperties"): for obj in prop_rel.RelatedObjects: if obj.is_a("IfcElement"): - string += obj.Name + " - " - string += quantity.Name + prefix += (obj.Name or "") + " - " + name = prefix + (quantity.Name or "") if quantity.is_a("IfcPhysicalSimpleQuantity"): - string += '", ' + str(quantity[3]) + "]," + 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]) else: - string += ' ERROR: Only IfcPhysicalSimpleQuantity is supported", 0.0],' - string = string.removesuffix(",") - string += "]" - return string + result.append([name + " ERROR: Only IfcPhysicalSimpleQuantity is supported", 0.0]) + return json.dumps(result, ensure_ascii=False) class SheetData(TypedDict): diff --git a/src/ifc5d/test/test_csv2ifc.py b/src/ifc5d/test/test_csv2ifc.py index c14a795da3..a630bb44ac 100644 --- a/src/ifc5d/test/test_csv2ifc.py +++ b/src/ifc5d/test/test_csv2ifc.py @@ -17,6 +17,7 @@ # along with IfcOpenShell. If not, see . import csv +import json import tempfile from pathlib import Path @@ -118,3 +119,25 @@ class TestCsv2Ifc: writer.write() assert len(list(Path(temp_csv_dir).glob("*.ods"))) == 1 assert len(list(Path(temp_csv_dir).glob("*.xlsx"))) == 1 + + +class TestSerialiseCostQuantities: + def test_quantity_name_with_special_characters_round_trips_as_json(self): + ifc_file = ifcopenshell.file() + name = 'Prospetto est "Np=256,667-23"' + quantity = ifc_file.create_entity("IfcQuantityArea", Name=name, AreaValue=12.5) + cost_item = ifc_file.create_entity("IfcCostItem", CostQuantities=[quantity]) + + result = ifc5d.ifc5Dspreadsheet.IfcDataGetter.serialise_cost_quantities(ifc_file, cost_item) + + assert json.loads(result) == [[name, 12.5]] + + def test_unset_name_does_not_crash(self): + ifc_file = ifcopenshell.file() + # Name left unset so quantity.Name resolves to None at access time. + quantity = ifc_file.create_entity("IfcQuantityArea", AreaValue=3.0) + cost_item = ifc_file.create_entity("IfcCostItem", CostQuantities=[quantity]) + + result = ifc5d.ifc5Dspreadsheet.IfcDataGetter.serialise_cost_quantities(ifc_file, cost_item) + + assert json.loads(result) == [["", 3.0]]