mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
See #3587. IfcCSV now supports custom header titles.
This commit is contained in:
@@ -28,6 +28,7 @@ classes = (
|
||||
operator.SelectCsvIfcFile,
|
||||
operator.ImportCsvAttributes,
|
||||
operator.ExportCsvAttributes,
|
||||
prop.CsvAttribute,
|
||||
prop.CsvProperties,
|
||||
ui.BIM_PT_ifccsv,
|
||||
)
|
||||
|
||||
@@ -71,19 +71,15 @@ class ImportCsvAttributes(bpy.types.Operator):
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
csv_props = context.scene.CsvProperties
|
||||
csv_json = json.load(open(self.filepath))
|
||||
|
||||
expression = csv_json.get("expression", "")
|
||||
if expression:
|
||||
csv_props.ifc_selector = expression
|
||||
|
||||
attributes = csv_json.get("attributes", [])
|
||||
if attributes:
|
||||
csv_props.csv_attributes.clear()
|
||||
for attribute in attributes:
|
||||
csv_props.csv_attributes.add().name = attribute
|
||||
props = context.scene.CsvProperties
|
||||
data = json.load(open(self.filepath))
|
||||
tool.Search.import_filter_query(data["query"], props.filter_groups)
|
||||
|
||||
props.csv_attributes.clear()
|
||||
for i, attribute in enumerate(data["attributes"]):
|
||||
new = props.csv_attributes.add()
|
||||
new.name = attribute
|
||||
new.header = data["headers"][i]
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -101,22 +97,16 @@ class ExportCsvAttributes(bpy.types.Operator):
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
csv_props = context.scene.CsvProperties
|
||||
props = context.scene.CsvProperties
|
||||
|
||||
csv_template = {}
|
||||
expression = csv_props.ifc_selector
|
||||
if expression:
|
||||
csv_template["expression"] = expression
|
||||
|
||||
csv_attributes = []
|
||||
for attribute in csv_props.csv_attributes:
|
||||
attribute_name = attribute.name
|
||||
csv_attributes.append(attribute_name)
|
||||
if csv_attributes:
|
||||
csv_template["attributes"] = csv_attributes
|
||||
data = {
|
||||
"query": tool.Search.export_filter_query(props.filter_groups),
|
||||
"attributes": [a.name for a in props.csv_attributes],
|
||||
"headers": [a.header for a in props.csv_attributes],
|
||||
}
|
||||
|
||||
with open(self.filepath, "w") as outfile:
|
||||
json.dump(csv_template, outfile)
|
||||
json.dump(data, outfile)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -155,11 +145,13 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
attributes = [a.name for a in props.csv_attributes]
|
||||
headers = [a.header 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,
|
||||
headers=headers,
|
||||
output=self.filepath,
|
||||
format=props.format,
|
||||
delimiter=sep,
|
||||
@@ -190,7 +182,8 @@ 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, null=props.null_value)
|
||||
attributes = [a.name for a in props.csv_attributes]
|
||||
ifc_csv.Import(ifc_file, self.filepath, attributes=attributes, delimiter=sep, null=props.null_value)
|
||||
if not props.should_load_from_memory:
|
||||
ifc_file.write(props.csv_ifc_file)
|
||||
purge_module_data()
|
||||
|
||||
@@ -32,6 +32,11 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
class CsvAttribute(PropertyGroup):
|
||||
name: StringProperty(name="Query", default="class")
|
||||
header: StringProperty(name="Header Value", default="IFC Class")
|
||||
|
||||
|
||||
class CsvProperties(PropertyGroup):
|
||||
csv_ifc_file: StringProperty(default="", name="IFC File")
|
||||
ifc_selector: StringProperty(default="", name="IFC Selector")
|
||||
@@ -48,7 +53,7 @@ class CsvProperties(PropertyGroup):
|
||||
("instance", "GlobalId", "", "GRIP", 7),
|
||||
],
|
||||
)
|
||||
csv_attributes: CollectionProperty(name="CSV Attributes", type=StrProperty)
|
||||
csv_attributes: CollectionProperty(name="CSV Attributes", type=CsvAttribute)
|
||||
null_value: StringProperty(default="N/A", name="Null Value")
|
||||
csv_delimiter: EnumProperty(
|
||||
items=[
|
||||
|
||||
@@ -78,6 +78,7 @@ class BIM_PT_ifccsv(Panel):
|
||||
for index, attribute in enumerate(props.csv_attributes):
|
||||
row = layout.row(align=True)
|
||||
row.prop(attribute, "name", text="")
|
||||
row.prop(attribute, "header", text="")
|
||||
row.operator("bim.remove_csv_attribute", icon="X", text="").index = index
|
||||
|
||||
row = layout.row(align=True)
|
||||
|
||||
+28
-8
@@ -54,9 +54,11 @@ class IfcCsv:
|
||||
self.results = []
|
||||
self.dataframe = None
|
||||
|
||||
def export(self, ifc_file, elements, attributes, output=None, format=None, delimiter=",", null=None):
|
||||
def export(self, ifc_file, elements, attributes, headers=None, output=None, format=None, delimiter=",", null=None):
|
||||
self.ifc_file = ifc_file
|
||||
self.results = []
|
||||
if not headers:
|
||||
headers = [None] * len(attributes)
|
||||
for element in elements:
|
||||
result = []
|
||||
if hasattr(element, "GlobalId"):
|
||||
@@ -77,7 +79,11 @@ class IfcCsv:
|
||||
self.results.append(result)
|
||||
|
||||
self.headers = ["GlobalId"]
|
||||
self.headers.extend(attributes or [])
|
||||
for i, attribute in enumerate(attributes or []):
|
||||
if headers[i]:
|
||||
self.headers.append(headers[i])
|
||||
else:
|
||||
self.headers.append(attribute)
|
||||
|
||||
if format == "csv":
|
||||
self.export_csv(output, delimiter=delimiter)
|
||||
@@ -184,7 +190,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=",", null="-"):
|
||||
def Import(self, ifc_file, table, attributes=None, delimiter=",", null="-"):
|
||||
# Currently only supports CSV.
|
||||
with open(table, newline="", encoding="utf-8") as f:
|
||||
reader = csv.reader(f, delimiter=delimiter)
|
||||
@@ -192,6 +198,12 @@ class IfcCsv:
|
||||
for row in reader:
|
||||
if not headers:
|
||||
headers = row
|
||||
print("csv", headers)
|
||||
if not attributes:
|
||||
attributes = [None] * len(headers)
|
||||
elif len(attributes) == len(headers) - 1:
|
||||
attributes.insert(0, "") # The GlobalId column
|
||||
print("hea", attributes)
|
||||
continue
|
||||
try:
|
||||
element = ifc_file.by_guid(row[0])
|
||||
@@ -203,7 +215,8 @@ class IfcCsv:
|
||||
continue # Skip GlobalId
|
||||
if value == null:
|
||||
value = None
|
||||
ifcopenshell.util.selector.set_element_value(ifc_file, element, headers[i], value)
|
||||
key = attributes[i] or headers[i]
|
||||
ifcopenshell.util.selector.set_element_value(ifc_file, element, key, value)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -218,9 +231,15 @@ if __name__ == "__main__":
|
||||
parser.add_argument("-q", "--query", type=str, default="", help='Specify a IFC query selector, such as "IfcWall"')
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--arguments",
|
||||
"--attributes",
|
||||
nargs="+",
|
||||
help="Specify attributes that are part of the extract, using the IfcQuery syntax such as 'type', 'Name' or 'Pset_Foo.Bar'",
|
||||
help="Specify attributes that are part of the extract, using the IfcQuery syntax such as 'class', 'Name' or 'Pset_Foo.Bar'",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-h",
|
||||
"--headers",
|
||||
nargs="+",
|
||||
help="Specify human readable headers that correlate to each attribute.",
|
||||
)
|
||||
parser.add_argument("--export", action="store_true", help="Export from IFC to CSV")
|
||||
parser.add_argument("--import", action="store_true", help="Import from CSV to IFC")
|
||||
@@ -233,7 +252,8 @@ if __name__ == "__main__":
|
||||
ifc_csv.export(
|
||||
ifc_file,
|
||||
results,
|
||||
args.arguments or [],
|
||||
args.attributes or [],
|
||||
headers=args.headers or [],
|
||||
output=args.spreadsheet,
|
||||
format=args.format,
|
||||
delimiter=args.delimiter,
|
||||
@@ -242,5 +262,5 @@ if __name__ == "__main__":
|
||||
elif getattr(args, "import"):
|
||||
ifc_csv = IfcCsv()
|
||||
ifc_file = ifcopenshell.open(args.ifc)
|
||||
ifc_csv.Import(ifc_file, args.spreadsheet, delimiter=args.delimiter, null=args.null)
|
||||
ifc_csv.Import(ifc_file, args.spreadsheet, attributes=args.attributes or [], delimiter=args.delimiter, null=args.null)
|
||||
ifc_file.write(args.ifc)
|
||||
|
||||
@@ -152,7 +152,8 @@ def set_element_value(ifc_file, element, query, value):
|
||||
elif key == "container":
|
||||
element = ifcopenshell.util.element.get_container(element)
|
||||
elif key == "class":
|
||||
return ifcopenshell.util.schema.reassign_class(ifc_file, element, value)
|
||||
if element.is_a() != value:
|
||||
return ifcopenshell.util.schema.reassign_class(ifc_file, element, value)
|
||||
elif key == "id":
|
||||
return
|
||||
elif isinstance(element, ifcopenshell.entity_instance):
|
||||
@@ -160,7 +161,8 @@ def set_element_value(ifc_file, element, query, value):
|
||||
key = "LayerSetName" # This oddity in the IFC spec is annoying so we account for it.
|
||||
|
||||
if hasattr(element, key):
|
||||
return setattr(element, key, value)
|
||||
if getattr(element, key) != value:
|
||||
return setattr(element, key, value)
|
||||
else:
|
||||
# Try to extract pset
|
||||
if is_regex:
|
||||
@@ -183,18 +185,16 @@ def set_element_value(ifc_file, element, query, value):
|
||||
element = result
|
||||
elif isinstance(element, dict): # Such as from the result of a prior get_pset
|
||||
pset = ifc_file.by_id(element["id"])
|
||||
if value in "NULL":
|
||||
value = None
|
||||
if is_regex:
|
||||
for prop in element.keys():
|
||||
for prop, prop_value in element.items():
|
||||
if re.match(key, prop):
|
||||
if pset.is_a("IfcPropertySet"):
|
||||
if pset.is_a("IfcPropertySet") and prop_value != value:
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc_file, pset=pset, properties={prop: value})
|
||||
elif pset.is_a("IfcElementQuantity"):
|
||||
elif pset.is_a("IfcElementQuantity") and prop_value != float(value):
|
||||
ifcopenshell.api.run("pset.edit_qto", ifc_file, qto=pset, properties={prop: float(value)})
|
||||
elif pset.is_a("IfcPropertySet"):
|
||||
elif pset.is_a("IfcPropertySet") and element[key] != value:
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc_file, pset=pset, properties={key: value})
|
||||
elif pset.is_a("IfcElementQuantity"):
|
||||
elif pset.is_a("IfcElementQuantity") and element[key] != float(value):
|
||||
ifcopenshell.api.run("pset.edit_qto", ifc_file, qto=pset, properties={key: float(value)})
|
||||
return
|
||||
elif isinstance(element, (list, tuple)): # If we use regex
|
||||
|
||||
Reference in New Issue
Block a user