Skip to content

Commit e222438

Browse files
committed
You can now customise the display of empty strings in IfcCSV.
1 parent 6ad51eb commit e222438

5 files changed

Lines changed: 40 additions & 19 deletions

File tree

src/blenderbim/blenderbim/bim/module/csv/operator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ def execute(self, context):
205205
delimiter=sep,
206206
include_global_id=props.include_global_id,
207207
null=props.null_value,
208+
empty=props.empty_value,
208209
bool_true=props.true_value,
209210
bool_false=props.false_value,
210211
sort=sort,
@@ -244,6 +245,7 @@ def execute(self, context):
244245
attributes=attributes,
245246
delimiter=sep,
246247
null=props.null_value,
248+
empty=props.empty_value,
247249
bool_true=props.true_value,
248250
bool_false=props.false_value,
249251
)

src/blenderbim/blenderbim/bim/module/csv/prop.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class CsvProperties(PropertyGroup):
6969
should_preserve_existing: BoolProperty(default=False, name="Preserve Existing")
7070
include_global_id: BoolProperty(default=True, name="Include GlobalId")
7171
null_value: StringProperty(default="N/A", name="Null Value")
72+
empty_value: StringProperty(default="-", name="Empty String Value")
7273
true_value: StringProperty(default="YES", name="True Value")
7374
false_value: StringProperty(default="NO", name="False Value")
7475
csv_delimiter: EnumProperty(

src/blenderbim/blenderbim/bim/module/csv/ui.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ def draw(self, context):
7474
row = layout.row()
7575
row.prop(props, "null_value")
7676
row = layout.row()
77+
row.prop(props, "empty_value")
78+
row = layout.row()
7779
row.prop(props, "true_value")
7880
row = layout.row()
7981
row.prop(props, "false_value")

src/ifccsv/ifccsv.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def export(
7070
include_global_id=True,
7171
delimiter=",",
7272
null="-",
73+
empty="",
7374
bool_true="YES",
7475
bool_false="NO",
7576
sort=None,
@@ -96,6 +97,8 @@ def export(
9697
value = ifcopenshell.util.selector.get_element_value(element, attribute)
9798
if value is None:
9899
value = null
100+
elif value == "":
101+
value = empty
99102
elif value is True:
100103
value = bool_true
101104
elif value is False:
@@ -368,17 +371,17 @@ def get_wildcard_attributes(self, attribute):
368371
results.update([p.Name for p in element.Quantities])
369372
return ["{}.{}".format(pset_qto_name, n) for n in results]
370373

371-
def Import(self, ifc_file, table, attributes=None, delimiter=",", null="-", bool_true="YES", bool_false="NO"):
374+
def Import(self, ifc_file, table, attributes=None, delimiter=",", null="-", empty="", bool_true="YES", bool_false="NO"):
372375
ext = table.split(".")[-1].lower()
373376

374377
if ext == "csv":
375-
self.import_csv(ifc_file, table, attributes, delimiter, null, bool_true, bool_false)
378+
self.import_csv(ifc_file, table, attributes, delimiter, null, empty, bool_true, bool_false)
376379
elif ext == "ods":
377-
self.import_ods(ifc_file, table, attributes, null, bool_true, bool_false)
380+
self.import_ods(ifc_file, table, attributes, null, empty, bool_true, bool_false)
378381
elif ext == "xlsx":
379-
self.import_xlsx(ifc_file, table, attributes, null, bool_true, bool_false)
382+
self.import_xlsx(ifc_file, table, attributes, null, empty, bool_true, bool_false)
380383

381-
def import_csv(self, ifc_file, table, attributes=None, delimiter=",", null="-", bool_true="YES", bool_false="NO"):
384+
def import_csv(self, ifc_file, table, attributes=None, delimiter=",", null="-", empty="", bool_true="YES", bool_false="NO"):
382385
with open(table, newline="", encoding="utf-8") as f:
383386
reader = csv.reader(f, delimiter=delimiter)
384387
headers = []
@@ -390,17 +393,17 @@ def import_csv(self, ifc_file, table, attributes=None, delimiter=",", null="-",
390393
elif len(attributes) == len(headers) - 1:
391394
attributes.insert(0, "") # The GlobalId column
392395
continue
393-
self.process_row(ifc_file, row, headers, attributes, null, bool_true, bool_false)
396+
self.process_row(ifc_file, row, headers, attributes, null, empty, bool_true, bool_false)
394397

395-
def import_xlsx(self, ifc_file, table, attributes, null, bool_true, bool_false):
398+
def import_xlsx(self, ifc_file, table, attributes, null, empty, bool_true, bool_false):
396399
df = pd.read_excel(table)
397-
self.import_pd(ifc_file, df, attributes, null, bool_true, bool_false)
400+
self.import_pd(ifc_file, df, attributes, null, empty, bool_true, bool_false)
398401

399-
def import_ods(self, ifc_file, table, attributes, null, bool_true, bool_false):
402+
def import_ods(self, ifc_file, table, attributes, null, empty, bool_true, bool_false):
400403
df = pd.read_excel(table, engine="odf")
401-
self.import_pd(ifc_file, df, attributes, null, bool_true, bool_false)
404+
self.import_pd(ifc_file, df, attributes, null, empty, bool_true, bool_false)
402405

403-
def import_pd(self, ifc_file, df, attributes=None, null="-", bool_true="YES", bool_false="NO"):
406+
def import_pd(self, ifc_file, df, attributes=None, null="-", empty="", bool_true="YES", bool_false="NO"):
404407
headers = df.columns.tolist()
405408

406409
if not attributes:
@@ -409,9 +412,9 @@ def import_pd(self, ifc_file, df, attributes=None, null="-", bool_true="YES", bo
409412
attributes.insert(0, "") # The GlobalId column
410413

411414
for _, row in df.iterrows():
412-
self.process_row(ifc_file, row.tolist(), headers, attributes, null, bool_true, bool_false)
415+
self.process_row(ifc_file, row.tolist(), headers, attributes, null, empty, bool_true, bool_false)
413416

414-
def process_row(self, ifc_file, row, headers, attributes, null, bool_true, bool_false):
417+
def process_row(self, ifc_file, row, headers, attributes, null, empty, bool_true, bool_false):
415418
try:
416419
element = ifc_file.by_guid(row[0])
417420
except:
@@ -422,6 +425,8 @@ def process_row(self, ifc_file, row, headers, attributes, null, bool_true, bool_
422425
continue # Skip GlobalId
423426
if value == null:
424427
value = None
428+
elif value == empty:
429+
value = ""
425430
elif value == bool_true:
426431
value = True
427432
elif value == bool_false:
@@ -449,7 +454,7 @@ def process_row(self, ifc_file, row, headers, attributes, null, bool_true, bool_
449454
help="Specify attributes that are part of the extract, using the IfcQuery syntax such as 'class', 'Name' or 'Pset_Foo.Bar'",
450455
)
451456
parser.add_argument(
452-
"-h", "--headers", nargs="+", help="Specify human readable headers that correlate to each attribute."
457+
"--headers", nargs="+", help="Specify human readable headers that correlate to each attribute."
453458
)
454459
parser.add_argument("--sort", nargs="+", help="Specify one or more attributes to sort by.")
455460
parser.add_argument("--order", nargs="+", help="Choose the sort order from ASC or DESC for each sorted attribute.")

src/ifcopenshell-python/docs/ifccsv.rst

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ utility:
6666
::
6767

6868
$ python -m ifccsv -h
69-
usage: ifccsv.py [-h] -i IFC [-s SPREADSHEET] [-f FORMAT] [-d DELIMITER] [-n NULL] [-q QUERY] [-a ARGUMENTS [ARGUMENTS ...]] [--export] [--import]
69+
usage: ifccsv.py [-h] -i IFC [-s SPREADSHEET] [-f FORMAT] [-d DELIMITER] [-n NULL] [--bool_true BOOL_TRUE] [--bool_false BOOL_FALSE] [-q QUERY] [-a ATTRIBUTES [ATTRIBUTES ...]] [--headers HEADERS [HEADERS ...]] [--sort SORT [SORT ...]]
70+
[--order ORDER [ORDER ...]] [--export] [--import]
7071

7172
Exports IFC data to and from CSV
7273

@@ -80,12 +81,22 @@ utility:
8081
-d DELIMITER, --delimiter DELIMITER
8182
The delimiter in CSV. Defaults to a comma.
8283
-n NULL, --null NULL How to represent null values. Defaults to a hyphen.
84+
--bool_true BOOL_TRUE
85+
How to represent true values. Defaults to YES.
86+
--bool_false BOOL_FALSE
87+
How to represent false values. Defaults to NO.
8388
-q QUERY, --query QUERY
8489
Specify a IFC query selector, such as "IfcWall"
85-
-a ARGUMENTS [ARGUMENTS ...], --arguments ARGUMENTS [ARGUMENTS ...]
86-
Specify attributes that are part of the extract, using the IfcQuery syntax such as 'type', 'Name' or 'Pset_Foo.Bar'
87-
--export Export from IFC to CSV
88-
--import Import from CSV to IFC
90+
-a ATTRIBUTES [ATTRIBUTES ...], --attributes ATTRIBUTES [ATTRIBUTES ...]
91+
Specify attributes that are part of the extract, using the IfcQuery syntax such as 'class', 'Name' or 'Pset_Foo.Bar'
92+
--headers HEADERS [HEADERS ...]
93+
Specify human readable headers that correlate to each attribute.
94+
--sort SORT [SORT ...]
95+
Specify one or more attributes to sort by.
96+
--order ORDER [ORDER ...]
97+
Choose the sort order from ASC or DESC for each sorted attribute.
98+
--export Export from IFC to the desired format.
99+
--import Import from the autodetected format to IFC.
89100
$ python -m ifccsv -i model.ifc -s out.csv -f csv -q .IfcProduct -a "Name" "Description" --export
90101
$ cat out.csv
91102

0 commit comments

Comments
 (0)