From a3db33411c7a120746b22e480f285efbdd2dc104 Mon Sep 17 00:00:00 2001 From: Petru Conduraru Date: Sat, 11 Jul 2026 15:22:08 +0300 Subject: [PATCH] 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 --- src/ifccsv/ifccsv.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/ifccsv/ifccsv.py b/src/ifccsv/ifccsv.py index f6d3157f81..45f17ac099 100755 --- a/src/ifccsv/ifccsv.py +++ b/src/ifccsv/ifccsv.py @@ -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]