mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Fix #3591. Fix bug where IfcCSV import was not symmetric capability wise. Now you can set all sorts of complex values with the exact same keys you are using to query.
This commit is contained in:
+1
-71
@@ -48,76 +48,6 @@ except:
|
||||
pass # No Pandas support
|
||||
|
||||
|
||||
class IfcAttributeSetter:
|
||||
@staticmethod
|
||||
def set_element_key(ifc_file, element, key, value):
|
||||
if key == "class" and element.is_a() != value:
|
||||
return ifcopenshell.util.schema.reassign_class(ifc_file, element, value)
|
||||
if hasattr(element, key):
|
||||
setattr(element, key, value)
|
||||
return element
|
||||
if "." not in key:
|
||||
return element
|
||||
pset_name, prop = key.split(".", 1)
|
||||
pset = ifcopenshell.util.element.get_pset(element, pset_name, should_inherit=True)
|
||||
if pset:
|
||||
pset = ifc_file.by_id(pset["id"])
|
||||
if pset.is_a("IfcElementQuantity"):
|
||||
IfcAttributeSetter.set_qto_property(pset, prop, value)
|
||||
else:
|
||||
IfcAttributeSetter.set_pset_property(ifc_file, pset, prop, value)
|
||||
return element
|
||||
|
||||
@staticmethod
|
||||
def set_qto_property(qto, name, value):
|
||||
for prop in qto.Quantities:
|
||||
if prop.Name != name:
|
||||
continue
|
||||
try:
|
||||
setattr(prop, prop.is_a()[len("IfcQuantity") :] + "Value", float(value))
|
||||
except:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def set_pset_property(ifc_file, pset, name, value):
|
||||
for property in pset.HasProperties:
|
||||
if property.Name != name:
|
||||
continue
|
||||
|
||||
if value.lower() in ["null", "none"]:
|
||||
property.NominalValue = None
|
||||
continue
|
||||
|
||||
# In lieu of loading a map for data casting, we only have four
|
||||
# options, which this ugly method will determine.
|
||||
if property.NominalValue is None:
|
||||
if value.isnumeric():
|
||||
property.NominalValue = ifc_file.createIfcInteger(int(value))
|
||||
elif value.lower() in ["1", "true", "yes", "uh-huh"]:
|
||||
property.NominalValue = ifc_file.createIfcBoolean(True)
|
||||
elif value.lower() in ["0", "false", "no", "nope"]:
|
||||
property.NominalValue = ifc_file.createIfcBoolean(False)
|
||||
else:
|
||||
try:
|
||||
value = float(value)
|
||||
property.NominalValue = ifc_file.createIfcReal(value)
|
||||
except:
|
||||
property.NominalValue = ifc_file.createIfcLabel(str(value))
|
||||
else:
|
||||
try:
|
||||
property.NominalValue.wrappedValue = str(value)
|
||||
except:
|
||||
try:
|
||||
property.NominalValue.wrappedValue = float(value)
|
||||
except:
|
||||
try:
|
||||
property.NominalValue.wrappedValue = int(value)
|
||||
except:
|
||||
property.NominalValue.wrappedValue = (
|
||||
True if value.lower() in ["1", "true", "yes", "uh-huh"] else False
|
||||
)
|
||||
|
||||
|
||||
class IfcCsv:
|
||||
def __init__(self, output="", delimiter=","):
|
||||
self.headers = []
|
||||
@@ -268,7 +198,7 @@ class IfcCsv:
|
||||
for i, value in enumerate(row):
|
||||
if i == 0:
|
||||
continue # Skip GlobalId
|
||||
element = IfcAttributeSetter.set_element_key(ifc_file, element, headers[i], value)
|
||||
ifcopenshell.util.selector.set_element_value(ifc_file, element, headers[i], value)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -24,7 +24,8 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.classification
|
||||
|
||||
|
||||
filter_elements_grammar = lark.Lark("""start: filter_group
|
||||
filter_elements_grammar = lark.Lark(
|
||||
"""start: filter_group
|
||||
filter_group: facet_list ("+" facet_list)*
|
||||
facet_list: facet ("," facet)*
|
||||
|
||||
@@ -86,9 +87,11 @@ filter_elements_grammar = lark.Lark("""start: filter_group
|
||||
NEWLINE: (CR? LF)+
|
||||
|
||||
%ignore WS // Disregard spaces in text
|
||||
""")
|
||||
"""
|
||||
)
|
||||
|
||||
get_element_grammar = lark.Lark("""start: WORD | ESCAPED_STRING | keys_regex | keys_quoted | keys_simple
|
||||
get_element_grammar = lark.Lark(
|
||||
"""start: WORD | ESCAPED_STRING | keys_regex | keys_quoted | keys_simple
|
||||
keys_regex: "r" ESCAPED_STRING ("." ESCAPED_STRING)*
|
||||
keys_quoted: ESCAPED_STRING ("." ESCAPED_STRING)*
|
||||
keys_simple: /[^\\W][^.=<>!%*\\]]*/ ("." /[^\\W][^.=<>!%*\\]]*/)*
|
||||
@@ -104,7 +107,8 @@ get_element_grammar = lark.Lark("""start: WORD | ESCAPED_STRING | keys_regex | k
|
||||
WS: /[ \\t\\f\\r\\n]/+
|
||||
|
||||
%ignore WS // Disregard spaces in text
|
||||
""")
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def get_element_value(element, query):
|
||||
@@ -120,6 +124,92 @@ def filter_elements(ifc_file, query, elements=None):
|
||||
return transformer.elements
|
||||
|
||||
|
||||
def set_element_value(ifc_file, element, query, value):
|
||||
start = get_element_grammar.parse(query)
|
||||
filter_query = Selector.parse_filter_query(start.children[0])
|
||||
keys = filter_query["keys"]
|
||||
is_regex = filter_query["is_regex"]
|
||||
|
||||
for i, key in enumerate(keys):
|
||||
key = key.strip()
|
||||
if element is None:
|
||||
return
|
||||
if key == "type":
|
||||
element = ifcopenshell.util.element.get_type(element)
|
||||
elif key in ("material", "mat"):
|
||||
element = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||
elif key in ("materials", "mats"):
|
||||
element = ifcopenshell.util.element.get_materials(element)
|
||||
elif key == "styles":
|
||||
element = ifcopenshell.util.element.get_styles(element)
|
||||
elif key in ("item", "i"):
|
||||
if element.is_a("IfcMaterialLayerSet"):
|
||||
element = element.MaterialLayers
|
||||
elif element.is_a("IfcMaterialProfileSet"):
|
||||
element = element.MaterialProfiles
|
||||
elif element.is_a("IfcMaterialConstituentSet"):
|
||||
element = element.MaterialConstituents
|
||||
elif key == "container":
|
||||
element = ifcopenshell.util.element.get_container(element)
|
||||
elif key == "class":
|
||||
return ifcopenshell.util.schema.reassign_class(ifc_file, element, value)
|
||||
elif key == "id":
|
||||
return
|
||||
elif isinstance(element, ifcopenshell.entity_instance):
|
||||
if key == "Name" and element.is_a("IfcMaterialLayerSet"):
|
||||
key = "LayerSetName" # This oddity in the IFC spec is annoying so we account for it.
|
||||
|
||||
if hasattr(element, key):
|
||||
return setattr(element, key, value)
|
||||
else:
|
||||
# Try to extract pset
|
||||
if is_regex:
|
||||
psets = ifcopenshell.util.element.get_psets(element)
|
||||
matching_psets = []
|
||||
for pset_name, pset in psets.items():
|
||||
if re.match(key, pset_name):
|
||||
matching_psets.append(pset)
|
||||
result = matching_psets or None
|
||||
else:
|
||||
result = ifcopenshell.util.element.get_pset(element, key)
|
||||
|
||||
if value and not result and len(keys) == i + 2: # The next key is the prop name
|
||||
if "qto" in key.lower() or "quantity" in key.lower() or "quantities" in key.lower():
|
||||
pset = ifcopenshell.api.run("pset.add_qto", ifc_file, product=element, name=key)
|
||||
else:
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc_file, product=element, name=key)
|
||||
result = {"id": pset.id()}
|
||||
|
||||
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():
|
||||
if re.match(key, prop):
|
||||
if pset.is_a("IfcPropertySet"):
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc_file, pset=pset, properties={prop: value})
|
||||
elif pset.is_a("IfcElementQuantity"):
|
||||
ifcopenshell.api.run("pset.edit_qto", ifc_file, qto=pset, properties={prop: float(value)})
|
||||
elif pset.is_a("IfcPropertySet"):
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc_file, pset=pset, properties={key: value})
|
||||
elif pset.is_a("IfcElementQuantity"):
|
||||
ifcopenshell.api.run("pset.edit_qto", ifc_file, qto=pset, properties={key: float(value)})
|
||||
return
|
||||
elif isinstance(element, (list, tuple)): # If we use regex
|
||||
if key.isnumeric():
|
||||
try:
|
||||
element = element[int(key)]
|
||||
except IndexError:
|
||||
return
|
||||
else:
|
||||
results = []
|
||||
for v in element:
|
||||
cls.set_element_value(v, keys[i + 1 :], is_regex=is_regex)
|
||||
return
|
||||
|
||||
|
||||
class FacetTransformer(lark.Transformer):
|
||||
def __init__(self, ifc_file, elements):
|
||||
self.file = ifc_file
|
||||
|
||||
Reference in New Issue
Block a user