mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Autoload active bsdd from IFC file
Now we store in the IFC file list of active bsdd user was using in Bonsai. So when you open IFC file it automatically marks dictionaries as active and there's no need to go to bsdd UI, unless there are new dictionaries to add/remove. Example - https://files.catbox.moe/kzwa2n.mp4
This commit is contained in:
@@ -38,8 +38,7 @@ class BSDDData:
|
||||
cls.data["active_dictionary"] = cls.active_dictionary()
|
||||
|
||||
@classmethod
|
||||
def active_dictionary(cls):
|
||||
props = tool.Bsdd.get_bsdd_props()
|
||||
def active_dictionary(cls) -> tool.Blender.BLENDER_ENUM_ITEMS:
|
||||
results = [("ALL", "All Dictionaries", "All active dictionaries")]
|
||||
results.extend([(d.uri, d.name, f"{d.status} - {d.version}") for d in props.dictionaries if d.is_active])
|
||||
results.extend(tool.Bsdd.get_active_bsdd_enum_items())
|
||||
return results
|
||||
|
||||
@@ -42,6 +42,8 @@ def get_active_dictionary(self: "BIMBSDDProperties", context: object) -> tool.Bl
|
||||
|
||||
|
||||
def update_is_active(self: "BSDDDictionary", context: bpy.types.Context) -> None:
|
||||
tool.Bsdd.save_active_bsdd_to_ifc()
|
||||
|
||||
BSDDData.data["active_dictionary"] = BSDDData.active_dictionary()
|
||||
if ClassificationsData.is_loaded:
|
||||
props = tool.Classification.get_classification_props()
|
||||
|
||||
@@ -65,13 +65,12 @@ class ClassificationsData:
|
||||
return [(str(e.id()), e.Name, "") for e in IfcStore.classification_file.by_type("IfcClassification")]
|
||||
|
||||
@classmethod
|
||||
def classification_source(cls):
|
||||
def classification_source(cls) -> tool.Blender.BLENDER_ENUM_ITEMS:
|
||||
items = [
|
||||
("FILE", "IFC File", ""),
|
||||
("MANUAL", "Manual Entry", ""),
|
||||
]
|
||||
bprops = tool.Bsdd.get_bsdd_props()
|
||||
dictionaries = [(d.uri, f"bSDD: {d.name}", "") for d in bprops.dictionaries if d.is_active]
|
||||
dictionaries = tool.Bsdd.get_active_bsdd_enum_items()
|
||||
if dictionaries:
|
||||
items.append(("BSDD", "All Active bSDDs", ""))
|
||||
items.extend(dictionaries)
|
||||
|
||||
@@ -22,13 +22,14 @@ import bonsai.tool as tool
|
||||
import bpy
|
||||
import json
|
||||
import bsdd
|
||||
import ifcopenshell.api.library
|
||||
import ifcopenshell.util.type
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.classification
|
||||
from typing import Any, Union, Optional, TYPE_CHECKING
|
||||
from typing import Any, Union, TYPE_CHECKING, TypedDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.bsdd.prop import BIMBSDDProperties
|
||||
from bonsai.bim.module.bsdd.prop import BIMBSDDProperties, BSDDDictionary
|
||||
|
||||
|
||||
class Bsdd(bonsai.core.tool.Bsdd):
|
||||
@@ -84,6 +85,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
|
||||
@classmethod
|
||||
def create_dictionaries(cls, dictionaries: list[bsdd.DictionaryContractV1]) -> None:
|
||||
props = cls.get_bsdd_props()
|
||||
active_bsdds: set[str] = {r.Location for r in cls.get_active_bsdd_ifc()}
|
||||
for dictionary in sorted(dictionaries, key=lambda d: d["name"]):
|
||||
new = props.dictionaries.add()
|
||||
new.name = dictionary["name"]
|
||||
@@ -92,6 +94,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
|
||||
new.organization_name_owner = dictionary["organizationNameOwner"]
|
||||
new.status = dictionary["status"]
|
||||
new.version = dictionary["version"]
|
||||
new["is_active"] = dictionary["uri"] in active_bsdds
|
||||
|
||||
@classmethod
|
||||
def get_active_class_data(cls) -> Union[bsdd.ClassContractV1, dict]:
|
||||
@@ -392,3 +395,80 @@ class Bsdd(bonsai.core.tool.Bsdd):
|
||||
uris.add(uri)
|
||||
class_uri, pset_name = pset_uri.rsplit("#", 1)
|
||||
return class_uri in uris
|
||||
|
||||
@classmethod
|
||||
def get_active_bsdd_library(cls) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for document in tool.Ifc.get().by_type("IfcLibraryInformation"):
|
||||
if document.Name == "BBIM_Active_bSDD":
|
||||
return document
|
||||
|
||||
@classmethod
|
||||
def get_refs(cls, library: ifcopenshell.entity_instance) -> tuple[ifcopenshell.entity_instance, ...]:
|
||||
if library.file.schema == "IFC2X3":
|
||||
return library.LibraryReference or ()
|
||||
return library.HasLibraryReferences
|
||||
|
||||
@classmethod
|
||||
def get_active_bsdd_ifc(cls) -> tuple[ifcopenshell.entity_instance, ...]:
|
||||
library = cls.get_active_bsdd_library()
|
||||
if not library:
|
||||
return ()
|
||||
return cls.get_refs(library)
|
||||
|
||||
class BSDDJsonData(TypedDict):
|
||||
name: str
|
||||
description: str
|
||||
|
||||
@classmethod
|
||||
def save_active_bsdd_to_ifc(cls) -> None:
|
||||
props = cls.get_bsdd_props()
|
||||
assert props.dictionaries
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
library = cls.get_active_bsdd_library()
|
||||
if not library:
|
||||
library = ifcopenshell.api.library.add_library(ifc_file, "BBIM_Active_bSDD")
|
||||
|
||||
active_bsdd: dict[str, BSDDDictionary] = {d.uri: d for d in props.dictionaries if d.is_active}
|
||||
refs_to_remove: list[ifcopenshell.entity_instance] = []
|
||||
active_bsdd_ifc: set[str] = set()
|
||||
for ref in cls.get_refs(library):
|
||||
uri = ref.Location
|
||||
if uri not in active_bsdd:
|
||||
refs_to_remove.append(ref)
|
||||
continue
|
||||
active_bsdd_ifc.add(uri)
|
||||
|
||||
# There is a possible edge case here.
|
||||
# E.g. user had some dicts from test/preview previously active.
|
||||
# But now other user opens file in Bonsai, where test/preview are not enabled in preferences,
|
||||
# and makes changes to active dictionaries.
|
||||
# Then, this will also unmark those test/preview dicts as active,
|
||||
# which can be sometimes confusing.
|
||||
#
|
||||
# But the alternative is to only change status just for the dictionary
|
||||
# changed by users, but then we need to come up with some other mechanism
|
||||
# how to keep this library clean from dead urls
|
||||
# (e.g. when dictionaries will go out of date).
|
||||
for ref in refs_to_remove:
|
||||
ifcopenshell.api.library.remove_reference(ifc_file, ref)
|
||||
|
||||
missing_bsdd = set(active_bsdd) - active_bsdd_ifc
|
||||
for uri in missing_bsdd:
|
||||
blender_dict = active_bsdd[uri]
|
||||
ref = ifcopenshell.api.library.add_reference(ifc_file, library)
|
||||
ref.Location = blender_dict.uri
|
||||
# Can't use Description as it's not present in IFC2X3.
|
||||
data: Bsdd.BSDDJsonData = {
|
||||
"name": blender_dict.name,
|
||||
"description": f"{blender_dict.status} - {blender_dict.version}",
|
||||
}
|
||||
ref.Name = json.dumps(data)
|
||||
|
||||
@classmethod
|
||||
def get_active_bsdd_enum_items(cls) -> list[tuple[str, str, str]]:
|
||||
results: list[tuple[str, str, str]] = []
|
||||
for ref in tool.Bsdd.get_active_bsdd_ifc():
|
||||
data: Bsdd.BSDDJsonData = json.loads(ref.Name)
|
||||
results.append((ref.Location, data["name"], data["description"]))
|
||||
return results
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.library
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.schema
|
||||
import bonsai.core.tool
|
||||
@@ -527,22 +529,20 @@ class TestSetupActiveBsddClassification(NewFile):
|
||||
props.export_schema = schema_
|
||||
bpy.ops.bim.create_project()
|
||||
ifc_file = tool.Ifc.get()
|
||||
name = "CCI Construction"
|
||||
data: tool.Bsdd.BSDDJsonData = {"name": "CCI Construction", "description": ""}
|
||||
base_uri = "https://identifier.buildingsmart.org/uri/molio/cciconstruction/1.0"
|
||||
if schema == "IFC2X3":
|
||||
uri = "https://identifier.buildingsmart.org/uri/molio/cciconstruction/1.0/class/A-BDC"
|
||||
classification = ifc_file.create_entity("IfcClassification", Name=name)
|
||||
ifc_file.create_entity("IfcClassificationReference", Location=uri, ReferencedSource=classification)
|
||||
else:
|
||||
attr_name = "Specification" if schema == "IFC4X3" else "Location"
|
||||
classification = ifc_file.create_entity("IfcClassification", Name=name)
|
||||
setattr(classification, attr_name, base_uri)
|
||||
|
||||
library = ifcopenshell.api.library.add_library(ifc_file, "BBIM_Active_bSDD")
|
||||
reference = ifcopenshell.api.library.add_reference(ifc_file, library)
|
||||
reference.Location = base_uri
|
||||
reference.Name = json.dumps(data)
|
||||
filepath = "test/files/temp/test.ifc"
|
||||
ifc_file.write(filepath)
|
||||
bpy.ops.bim.load_project(filepath=filepath)
|
||||
props = tool.Bsdd.get_bsdd_props()
|
||||
assert props.active_domain == name
|
||||
assert props.active_uri == base_uri
|
||||
props = tool.Classification.get_classification_props()
|
||||
|
||||
# No errors means this uri present in the enum.
|
||||
props.classification_source = base_uri
|
||||
|
||||
def test_set_load_and_set_active_bsdd_ifc2x3(self):
|
||||
self.run_test("IFC2X3")
|
||||
|
||||
Reference in New Issue
Block a user