mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 00:32:38 +00:00
Gets the pick list for bSDD properties with Allowed Values working
This commit is contained in:
@@ -170,7 +170,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
|
|||||||
psets.setdefault(pset, {})
|
psets.setdefault(pset, {})
|
||||||
|
|
||||||
predefined_value = prop.get("predefinedValue")
|
predefined_value = prop.get("predefinedValue")
|
||||||
if predefined_value:
|
if predefined_value and predefined_value != "None":
|
||||||
possible_values = [predefined_value]
|
possible_values = [predefined_value]
|
||||||
else:
|
else:
|
||||||
possible_values = prop.get("allowedValues", []) or []
|
possible_values = prop.get("allowedValues", []) or []
|
||||||
@@ -369,7 +369,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
|
|||||||
data = cls.bsdd_properties[bsdd_prop.uri]
|
data = cls.bsdd_properties[bsdd_prop.uri]
|
||||||
|
|
||||||
predefined_value = data.get("predefinedValue")
|
predefined_value = data.get("predefinedValue")
|
||||||
if predefined_value:
|
if predefined_value and predefined_value != "None":
|
||||||
possible_values = [predefined_value]
|
possible_values = [predefined_value]
|
||||||
else:
|
else:
|
||||||
possible_values = data.get("allowedValues", []) or []
|
possible_values = data.get("allowedValues", []) or []
|
||||||
@@ -401,7 +401,13 @@ class Bsdd(bonsai.core.tool.Bsdd):
|
|||||||
uris.add(uri)
|
uris.add(uri)
|
||||||
psets = set()
|
psets = set()
|
||||||
for uri in uris:
|
for uri in uris:
|
||||||
if not (bsdd_class := cls.bsdd_classes.get(uri, None)):
|
try:
|
||||||
|
# Cache may not be populated yet (e.g. a fresh session that never
|
||||||
|
# browsed this class), so fetch on a cache miss instead of skipping.
|
||||||
|
bsdd_class = cls.get_bsdd_class(uri)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if not bsdd_class:
|
||||||
continue
|
continue
|
||||||
for class_pset in bsdd_class.get("classProperties", []):
|
for class_pset in bsdd_class.get("classProperties", []):
|
||||||
if not (pset_name := class_pset.get("propertySet", None)):
|
if not (pset_name := class_pset.get("propertySet", None)):
|
||||||
@@ -409,6 +415,37 @@ class Bsdd(bonsai.core.tool.Bsdd):
|
|||||||
psets.add((uri, bsdd_class["name"], pset_name))
|
psets.add((uri, bsdd_class["name"], pset_name))
|
||||||
return psets
|
return psets
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_bsdd_pset_property_values(
|
||||||
|
cls, element: ifcopenshell.entity_instance, pset_name: str
|
||||||
|
) -> dict[str, list[str]]:
|
||||||
|
"""Map bSDD property code -> allowed/predefined values for properties bSDD says
|
||||||
|
are applicable to `element` (via its classification references) under `pset_name`.
|
||||||
|
|
||||||
|
Used to recognise a Pset property as bSDD-sourced even outside the dedicated
|
||||||
|
bSDD add-property flow (e.g. a Pset created in a previous session), so it can
|
||||||
|
still be edited as a picklist.
|
||||||
|
"""
|
||||||
|
result: dict[str, list[str]] = {}
|
||||||
|
for uri, class_name, applicable_pset_name in cls.get_applicable_psets(element):
|
||||||
|
if applicable_pset_name != pset_name:
|
||||||
|
continue
|
||||||
|
bsdd_class = cls.get_bsdd_class(uri)
|
||||||
|
for class_prop in bsdd_class.get("classProperties", []):
|
||||||
|
if class_prop.get("propertySet") != pset_name:
|
||||||
|
continue
|
||||||
|
code = class_prop.get("propertyCode")
|
||||||
|
if not code or code in result:
|
||||||
|
continue
|
||||||
|
predefined_value = class_prop.get("predefinedValue")
|
||||||
|
if predefined_value and predefined_value != "None":
|
||||||
|
result[code] = [predefined_value]
|
||||||
|
continue
|
||||||
|
allowed_values = class_prop.get("allowedValues", []) or []
|
||||||
|
if allowed_values:
|
||||||
|
result[code] = [v["value"] for v in allowed_values]
|
||||||
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_applicable(cls, pset_uri: str, element: ifcopenshell.entity_instance) -> bool:
|
def is_applicable(cls, pset_uri: str, element: ifcopenshell.entity_instance) -> bool:
|
||||||
uris = set()
|
uris = set()
|
||||||
|
|||||||
@@ -223,6 +223,18 @@ class Pset(bonsai.core.tool.Pset):
|
|||||||
elif pset.is_a("IfcMaterialProperties") or pset.is_a("IfcProfileProperties"):
|
elif pset.is_a("IfcMaterialProperties") or pset.is_a("IfcProfileProperties"):
|
||||||
pset_props = pset.Properties
|
pset_props = pset.Properties
|
||||||
|
|
||||||
|
# If this Pset's owning element is classified against a bSDD class that defines
|
||||||
|
# this Pset, recognise properties matching bSDD property codes as picklists,
|
||||||
|
# even though the Pset wasn't necessarily created through the bSDD add-property UI.
|
||||||
|
bsdd_allowed_values: dict[str, list[str]] = {}
|
||||||
|
if pset.is_a("IfcPropertySet"):
|
||||||
|
elements = ifcopenshell.util.element.get_elements_by_pset(pset)
|
||||||
|
if elements:
|
||||||
|
try:
|
||||||
|
bsdd_allowed_values = tool.Bsdd.get_bsdd_pset_property_values(next(iter(elements)), pset.Name)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
prop_templates: dict[str, ifcopenshell.entity_instance] = {}
|
prop_templates: dict[str, ifcopenshell.entity_instance] = {}
|
||||||
if pset_template:
|
if pset_template:
|
||||||
prop_templates = {prop.Name: prop for prop in pset_template.HasPropertyTemplates}
|
prop_templates = {prop.Name: prop for prop in pset_template.HasPropertyTemplates}
|
||||||
@@ -286,6 +298,17 @@ class Pset(bonsai.core.tool.Pset):
|
|||||||
metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
|
metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
|
||||||
process_prop_description(metadata)
|
process_prop_description(metadata)
|
||||||
|
|
||||||
|
if prop.is_a("IfcPropertySingleValue") and (possible_values := bsdd_allowed_values.get(prop.Name)):
|
||||||
|
str_value = None if value is None else str(value)
|
||||||
|
if str_value is not None and str_value not in possible_values:
|
||||||
|
# Preserve a legacy/imported value that doesn't match the current
|
||||||
|
# bSDD enumeration instead of silently dropping it.
|
||||||
|
possible_values = [*possible_values, str_value]
|
||||||
|
metadata.enum_items = json.dumps(possible_values)
|
||||||
|
metadata.data_type = "enum"
|
||||||
|
if str_value is not None:
|
||||||
|
metadata.enum_value = str_value
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_prop_template_primitive_type(cls, prop_template: ifcopenshell.entity_instance) -> str:
|
def get_prop_template_primitive_type(cls, prop_template: ifcopenshell.entity_instance) -> str:
|
||||||
if prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
|
if prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
|
||||||
|
|||||||
+1
-1
@@ -1026,7 +1026,7 @@ def apply_ifc_classification_properties(
|
|||||||
psets = ifcopenshell.util.element.get_psets(element)
|
psets = ifcopenshell.util.element.get_psets(element)
|
||||||
for prop in classificationProperties:
|
for prop in classificationProperties:
|
||||||
predefinedValue = prop.get("predefinedValue")
|
predefinedValue = prop.get("predefinedValue")
|
||||||
if not predefinedValue or prop.get("propertyDomainName") != "IFC":
|
if not predefinedValue or predefinedValue == "None" or prop.get("propertyDomainName") != "IFC":
|
||||||
continue
|
continue
|
||||||
pset = psets.get(prop["propertySet"])
|
pset = psets.get(prop["propertySet"])
|
||||||
if pset:
|
if pset:
|
||||||
|
|||||||
Reference in New Issue
Block a user