Expand wildcard attributes in IfcCsv export (#3424)

Exporting with a "*" attribute (or "Pset_Name.*") emitted a literal "*"
column filled with the null placeholder instead of expanding: the
get_wildcard_attributes helper had no callers after a refactor dropped the
expansion step.

Wire the expansion back in: before the per-element loop, export() now calls
expand_wildcard_attributes, which passes through non-wildcard queries
unchanged, expands a bare "*" to every direct attribute of the exported
elements (new get_element_attributes helper), and expands "Pset_Name.*" /
"Qto_Name.*" via the existing get_wildcard_attributes helper. Expanded
columns that duplicate an existing one (e.g. GlobalId or an explicit Name)
are skipped.

Verified headless: exporting an IfcWall with ["Name","*"] now yields real
attribute columns with values (W1, MyWall, T1, ...) instead of a literal
"*", "Pset_WallCommon.*" expands to that pset's properties, and an explicit
named-attribute export is unchanged.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Petru Conduraru
2026-07-11 15:22:08 +03:00
parent 256d5a63f1
commit a3db33411c
+41
View File
@@ -105,6 +105,9 @@ class IfcCsv:
attributes.insert(0, "GlobalId")
headers.insert(0, "GlobalId")
elements = list(elements)
attributes, headers = self.expand_wildcard_attributes(attributes, headers, elements)
for element in elements:
result = []
@@ -384,6 +387,44 @@ class IfcCsv:
self.dataframe = pd.DataFrame(self.results, columns=self.headers)
return self.dataframe
def expand_wildcard_attributes(self, attributes, headers, elements):
"""Expand any attribute containing ``*`` into concrete attribute names.
A bare ``*`` expands to every direct attribute of the exported elements.
A ``Pset_Name.*`` (or ``Qto_Name.*``) query expands to every property of
that named property set / quantity set, using ``get_wildcard_attributes``.
Attributes without a ``*`` are passed through unchanged so explicit
named queries keep their position and any custom header.
"""
expanded_attributes = []
expanded_headers = []
for attribute, header in zip(attributes, headers):
if "*" not in attribute:
expanded_attributes.append(attribute)
expanded_headers.append(header)
continue
if attribute == "*":
expansions = self.get_element_attributes(elements)
else:
expansions = self.get_wildcard_attributes(attribute)
for expanded in expansions:
if expanded in expanded_attributes:
continue # Avoid duplicate columns (e.g. GlobalId).
expanded_attributes.append(expanded)
# Expanded columns fall back to the attribute name as header.
expanded_headers.append(None)
return expanded_attributes, expanded_headers
def get_element_attributes(self, elements):
results = []
for element in elements:
for name in element.get_info(recursive=False):
if name in ("id", "type"):
continue
if name not in results:
results.append(name)
return results
def get_wildcard_attributes(self, attribute):
results = set()
pset_qto_name = attribute.split(".", 1)[0]