Skip to content

Commit 42b777b

Browse files
committed
application ui - support changing organization
1 parent a968cad commit 42b777b

File tree

6 files changed

+74
-8
lines changed

6 files changed

+74
-8
lines changed

src/bonsai/bonsai/bim/helper.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,15 @@ def export_attributes(
273273
return attributes
274274

275275

276+
def process_exported_entity_attribute(attributes: dict[str, Any], attribute_name: str) -> None:
277+
entity_id = attributes[attribute_name]
278+
if entity_id is None:
279+
# Maybe it was removed by now and enum is invalid.
280+
del attributes[attribute_name]
281+
else:
282+
attributes[attribute_name] = tool.Ifc.get().by_id(int(entity_id))
283+
284+
276285
ENUM_ITEMS_DATA = Union[bpy.types.PropertyGroup, bpy.types.ID, bpy.types.Operator, bpy.types.OperatorProperties]
277286

278287

src/bonsai/bonsai/bim/prop.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,13 @@ def cache_string(s: Any) -> str:
8484
cache_string.data: dict[str, str] = {}
8585

8686

87-
def get_attribute_enum_values(prop: "Attribute", context: bpy.types.Context) -> list[tuple[str, str, str]]:
87+
def get_attribute_enum_values(prop: "Attribute", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS:
88+
if "EnumData" not in globals() or TYPE_CHECKING:
89+
from bonsai.bim.ui import EnumData
90+
91+
if dynamic_identifier := prop.enum_items_dynamic:
92+
return EnumData.get_data(dynamic_identifier)
93+
8894
# Support weird buildingSMART dictionary mappings which behave like enums
8995
items: list[tuple[str, str, str]] = []
9096
data = json.loads(prop.enum_items)
@@ -314,6 +320,11 @@ class Attribute(PropertyGroup):
314320
name="Value", description=tooltip, get=get_length_value, set=set_length_value, unit="LENGTH"
315321
)
316322
enum_items: StringProperty(name="Value")
323+
"""Json serialized mapping of enum items:
324+
Typically a dictionary of string identifiers to item names.
325+
"""
326+
enum_items_dynamic: StringProperty()
327+
"""Dynamic enum items identifier."""
317328
enum_descriptions: CollectionProperty(type=StrProperty)
318329
enum_value: EnumProperty(items=get_attribute_enum_values, name="Value", update=update_attribute_value)
319330
filepath_value: PointerProperty(type=MultipleFileSelect)
@@ -343,6 +354,7 @@ class Attribute(PropertyGroup):
343354
float_value: float
344355
length_value: float
345356
enum_items: str
357+
enum_items_dynamic: str
346358
enum_descriptions: bpy.types.bpy_prop_collection_idprop[StrProperty]
347359
enum_value: str
348360
filepath_value: MultipleFileSelect
@@ -365,7 +377,11 @@ def get_value(self) -> Union[str, float, int, bool, None]:
365377
return self.string_value.replace("\\n", "\n")
366378
if self.data_type == "file":
367379
return [f.name for f in self.filepath_value.file_list]
368-
value = getattr(self, str(self.get_value_name()), None)
380+
value_name = self.get_value_name()
381+
if value_name == "enum_value":
382+
value = tool.Blender.get_enum_safe(self, "enum_value")
383+
else:
384+
value = getattr(self, value_name, None)
369385
if self.special_type == "LOGICAL" and value != "UNKNOWN":
370386
# IfcOpenShell expects bool if IfcLogical is True/False.
371387
value = value == "TRUE"

src/bonsai/bonsai/bim/ui.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from ifcopenshell.util.file import IfcHeaderExtractor
3737
from bonsai.bim.prop import Attribute
3838
from typing import Optional, TYPE_CHECKING
39+
from natsort import natsorted
3940

4041

4142
class IFCFileSelector:
@@ -1167,6 +1168,22 @@ def draw(self, context):
11671168

11681169
def refresh():
11691170
UIData.is_loaded = False
1171+
EnumData.data.clear()
1172+
1173+
1174+
class EnumData:
1175+
data: dict[str, tool.Blender.BLENDER_ENUM_ITEMS] = {}
1176+
1177+
@classmethod
1178+
def get_data(cls, identifier: str) -> tool.Blender.BLENDER_ENUM_ITEMS:
1179+
if identifier not in EnumData.data:
1180+
cls.data[identifier] = getattr(cls, identifier)()
1181+
return cls.data[identifier]
1182+
1183+
@classmethod
1184+
def organizations(cls) -> tool.Blender.BLENDER_ENUM_ITEMS:
1185+
organizations = tool.Ifc.get().by_type("IfcOrganization")
1186+
return natsorted(((str(e.id()), e.Name, "") for e in organizations), key=lambda x: x[1])
11701187

11711188

11721189
class UIData:

src/bonsai/bonsai/tool/blender.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class Blender(bonsai.core.tool.Blender):
9393
9494
- (identifier, name, description, icon, number)
9595
"""
96-
BLENDER_ENUM_ITEMS = list[BLENDER_ENUM_ITEM]
96+
BLENDER_ENUM_ITEMS = Iterable[BLENDER_ENUM_ITEM]
9797

9898
@classmethod
9999
def activate_camera(cls, obj: bpy.types.Object) -> None:

src/bonsai/bonsai/tool/owner.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
if TYPE_CHECKING:
2828
from bonsai.bim.module.owner.prop import BIMOwnerProperties
29+
from bonsai.bim.prop import Attribute
2930

3031

3132
class Owner(bonsai.core.tool.Owner):
@@ -327,10 +328,23 @@ def import_application_attributes(cls) -> None:
327328
application = tool.Ifc.get().by_id(props.active_application_id)
328329
props.application_attributes.clear()
329330

330-
bonsai.bim.helper.import_attributes("IfcApplication", props.application_attributes, application.get_info())
331+
def callback(name: str, prop: Union[Attribute, None], data: dict[str, Any]):
332+
if name == "ApplicationDeveloper":
333+
new = props.application_attributes.add()
334+
new.name = name
335+
new.data_type = "enum"
336+
new.is_optional = False
337+
new.enum_items_dynamic = "organizations"
338+
new.enum_value = str(data["ApplicationDeveloper"].id())
339+
return True
340+
341+
bonsai.bim.helper.import_attributes(
342+
"IfcApplication", props.application_attributes, application.get_info(), callback
343+
)
331344

332345
@classmethod
333346
def export_application_attributes(cls) -> dict[str, Any]:
334347
props = cls.get_owner_props()
335348
attributes = bonsai.bim.helper.export_attributes(props.application_attributes)
349+
bonsai.bim.helper.process_exported_entity_attribute(attributes, "ApplicationDeveloper")
336350
return attributes

src/bonsai/test/tool/test_owner.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import bonsai.tool as tool
2424
from test.bim.bootstrap import NewFile
2525
from bonsai.tool.owner import Owner as subject
26+
from typing import Any
27+
from functools import cache
2628

2729

2830
class TestImplementsTool(NewFile):
@@ -584,12 +586,18 @@ def test_run(self):
584586

585587
class TestApplicationUI(NewFile):
586588
ifc_class = "IfcApplication"
587-
attrs = {
589+
attrs_: dict[str, Any] = {
588590
"Version": "v001",
589591
"ApplicationFullName": "IfcOpenShell",
590592
"ApplicationIdentifier": "IfcOpenShell",
591593
}
592594

595+
@cache
596+
def get_attrs(self, ifc_file: ifcopenshell.file) -> dict[str, Any]:
597+
attrs = self.attrs_.copy()
598+
attrs["ApplicationDeveloper"] = ifc_file.create_entity("IfcOrganization", Name="Test")
599+
return attrs
600+
593601
def test_set(self):
594602
application = ifcopenshell.file().create_entity(self.ifc_class)
595603
subject.set_application(application)
@@ -614,14 +622,16 @@ def test_import(self):
614622
ifc = ifcopenshell.file()
615623
tool.Ifc.set(ifc)
616624
application = ifc.create_entity(self.ifc_class)
617-
for attr, value in self.attrs.items():
625+
for attr, value in self.get_attrs(ifc).items():
618626
setattr(application, attr, value)
619627
subject.set_application(application)
620628
subject.import_application_attributes()
621629
props = subject.get_owner_props()
622-
for attr, value in self.attrs.items():
630+
for attr, value in self.get_attrs(ifc).items():
631+
if isinstance(value, ifcopenshell.entity_instance):
632+
value = str(value.id())
623633
assert props.application_attributes[attr].get_value() == value
624634

625635
def test_export(self):
626636
self.test_import()
627-
assert subject.export_application_attributes() == self.attrs
637+
assert subject.export_application_attributes() == self.get_attrs(tool.Ifc.get())

0 commit comments

Comments
 (0)