diff --git a/src/blenderbim/blenderbim/bim/module/csv/operator.py b/src/blenderbim/blenderbim/bim/module/csv/operator.py index 256193d9c8..569e5e2820 100644 --- a/src/blenderbim/blenderbim/bim/module/csv/operator.py +++ b/src/blenderbim/blenderbim/bim/module/csv/operator.py @@ -153,7 +153,15 @@ class ExportIfcCsv(bpy.types.Operator): ifc_csv = ifccsv.IfcCsv() attributes = [a.name for a in props.csv_attributes] sep = props.csv_custom_delimiter if props.csv_delimiter == "CUSTOM" else props.csv_delimiter - ifc_csv.export(ifc_file, results, attributes, output=self.filepath, format=props.format, delimiter=sep) + ifc_csv.export( + ifc_file, + results, + attributes, + output=self.filepath, + format=props.format, + delimiter=sep, + null=props.null_value, + ) return {"FINISHED"} @@ -179,7 +187,7 @@ class ImportIfcCsv(bpy.types.Operator): ifc_file = ifcopenshell.open(props.csv_ifc_file) ifc_csv = ifccsv.IfcCsv() sep = props.csv_custom_delimiter if props.csv_delimiter == "CUSTOM" else props.csv_delimiter - ifc_csv.Import(ifc_file, self.filepath, delimiter=sep) + ifc_csv.Import(ifc_file, self.filepath, delimiter=sep, null=props.null_value) if not props.should_load_from_memory: ifc_file.write(props.csv_ifc_file) purge_module_data() diff --git a/src/blenderbim/blenderbim/bim/module/csv/prop.py b/src/blenderbim/blenderbim/bim/module/csv/prop.py index 4cdbe3ba7c..8259d56b32 100644 --- a/src/blenderbim/blenderbim/bim/module/csv/prop.py +++ b/src/blenderbim/blenderbim/bim/module/csv/prop.py @@ -35,6 +35,7 @@ class CsvProperties(PropertyGroup): csv_ifc_file: StringProperty(default="", name="IFC File") ifc_selector: StringProperty(default="", name="IFC Selector") csv_attributes: CollectionProperty(name="CSV Attributes", type=StrProperty) + null_value: StringProperty(default="N/A", name="Null Value") csv_delimiter: EnumProperty( items=[ (";", ";", ""), diff --git a/src/blenderbim/blenderbim/bim/module/csv/ui.py b/src/blenderbim/blenderbim/bim/module/csv/ui.py index 9f690a762a..f5bd4d4a94 100644 --- a/src/blenderbim/blenderbim/bim/module/csv/ui.py +++ b/src/blenderbim/blenderbim/bim/module/csv/ui.py @@ -72,6 +72,9 @@ class BIM_PT_ifccsv(Panel): row = layout.row(align=True) row.prop(props, "csv_custom_delimiter") + row = layout.row(align=True) + row.prop(props, "null_value") + row = layout.row() split = row.split(factor=0.5) c = split.column() diff --git a/src/ifccsv/ifccsv.py b/src/ifccsv/ifccsv.py index fb9a7ec7ce..88b6aa0b68 100755 --- a/src/ifccsv/ifccsv.py +++ b/src/ifccsv/ifccsv.py @@ -49,12 +49,12 @@ except: class IfcCsv: - def __init__(self, output="", delimiter=","): + def __init__(self): self.headers = [] self.results = [] self.dataframe = None - def export(self, ifc_file, elements, attributes, output=None, format=None, delimiter=","): + def export(self, ifc_file, elements, attributes, output=None, format=None, delimiter=",", null=None): self.ifc_file = ifc_file self.results = [] for element in elements: @@ -70,7 +70,10 @@ class IfcCsv: del attributes[index] for attribute in attributes: - result.append(ifcopenshell.util.selector.get_element_value(element, attribute)) + value = ifcopenshell.util.selector.get_element_value(element, attribute) + if value is None: + value = null + result.append(value) self.results.append(result) self.headers = ["GlobalId"] @@ -130,7 +133,7 @@ class IfcCsv: self.doc.spreadsheet.addElement(table) if output[-4:].lower() == ".ods": - output = output[0:-4] + output = output[0:-4] self.doc.save(output, True) def export_xlsx(self, output): @@ -181,7 +184,7 @@ class IfcCsv: results.update([p.Name for p in element.Quantities]) return ["{}.{}".format(pset_qto_name, n) for n in results] - def Import(self, ifc_file, table, delimiter=","): + def Import(self, ifc_file, table, delimiter=",", null="-"): # Currently only supports CSV. with open(table, newline="", encoding="utf-8") as f: reader = csv.reader(f, delimiter=delimiter) @@ -198,6 +201,8 @@ class IfcCsv: for i, value in enumerate(row): if i == 0: continue # Skip GlobalId + if value == null: + value = None ifcopenshell.util.selector.set_element_value(ifc_file, element, headers[i], value) @@ -206,6 +211,10 @@ if __name__ == "__main__": parser.add_argument("-i", "--ifc", type=str, required=True, help="The IFC file") parser.add_argument("-s", "--spreadsheet", type=str, default="data.csv", help="The spreadsheet file") parser.add_argument("-f", "--format", type=str, default="csv", help="The format, chosen from csv, ods, or xlsx") + parser.add_argument("-d", "--delimiter", type=str, default=",", help="The delimiter in CSV. Defaults to a comma.") + parser.add_argument( + "-n", "--null", type=str, default="-", help="How to represent null values. Defaults to a hyphen." + ) parser.add_argument("-q", "--query", type=str, default="", help='Specify a IFC query selector, such as "IfcWall"') parser.add_argument( "-a", @@ -221,9 +230,17 @@ if __name__ == "__main__": ifc_file = ifcopenshell.open(args.ifc) results = ifcopenshell.util.selector.filter_elements(ifc_file, args.query) ifc_csv = IfcCsv() - ifc_csv.export(ifc_file, results, args.arguments or [], output=args.spreadsheet, format=args.format) + ifc_csv.export( + ifc_file, + results, + args.arguments or [], + output=args.spreadsheet, + format=args.format, + delimiter=args.delimiter, + null=args.null, + ) elif getattr(args, "import"): ifc_csv = IfcCsv() ifc_file = ifcopenshell.open(args.ifc) - ifc_csv.Import(ifc_file, args.spreadsheet) + ifc_csv.Import(ifc_file, args.spreadsheet, delimiter=args.delimiter, null=args.null) ifc_file.write(args.ifc) diff --git a/src/ifcopenshell-python/docs/ifccsv.rst b/src/ifcopenshell-python/docs/ifccsv.rst index da2ecc8298..0e89bf9622 100644 --- a/src/ifcopenshell-python/docs/ifccsv.rst +++ b/src/ifcopenshell-python/docs/ifccsv.rst @@ -66,10 +66,10 @@ utility: :: $ python -m ifccsv -h - usage: ifccsv.py [-h] -i IFC [-s SPREADSHEET] [-f FORMAT] [-q QUERY] [-a ARGUMENTS [ARGUMENTS ...]] [--export] [--import] + usage: ifccsv.py [-h] -i IFC [-s SPREADSHEET] [-f FORMAT] [-d DELIMITER] [-n NULL] [-q QUERY] [-a ARGUMENTS [ARGUMENTS ...]] [--export] [--import] Exports IFC data to and from CSV - + options: -h, --help show this help message and exit -i IFC, --ifc IFC The IFC file @@ -77,8 +77,11 @@ utility: The spreadsheet file -f FORMAT, --format FORMAT The format, chosen from csv, ods, or xlsx + -d DELIMITER, --delimiter DELIMITER + The delimiter in CSV. Defaults to a comma. + -n NULL, --null NULL How to represent null values. Defaults to a hyphen. -q QUERY, --query QUERY - Specify a IFC query selector, such as ".IfcWall" + Specify a IFC query selector, such as "IfcWall" -a ARGUMENTS [ARGUMENTS ...], --arguments ARGUMENTS [ARGUMENTS ...] Specify attributes that are part of the extract, using the IfcQuery syntax such as 'type', 'Name' or 'Pset_Foo.Bar' --export Export from IFC to CSV @@ -101,7 +104,7 @@ Here is a minimal example of how to use IfcCSV as a library: # Export our model's elements and their attributes to a CSV. ifc_csv = IfcCsv() - ifc_csv.export(model, elements, attributes, output="out.csv", format="csv", delimiter=",") + ifc_csv.export(model, elements, attributes, output="out.csv", format="csv", delimiter=",", null="-") # Optionally, you can explicitly export to different formats. # ifc_csv = IfcCsv()