diff --git a/src/blenderbim/blenderbim/bim/module/bsdd/__init__.py b/src/blenderbim/blenderbim/bim/module/bsdd/__init__.py index 63f62931a6..fd521a6a45 100644 --- a/src/blenderbim/blenderbim/bim/module/bsdd/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/bsdd/__init__.py @@ -22,8 +22,8 @@ from . import ui, prop, operator classes = ( operator.GetBSDDClassificationProperties, operator.LoadBSDDDomains, - operator.SearchBSDDClassifications, - operator.SetActiveBSDDDomain, + operator.SearchBSDDClass, + operator.SetActiveBSDDDictionary, prop.BSDDDomain, prop.BSDDClassification, prop.BSDDPset, diff --git a/src/blenderbim/blenderbim/bim/module/bsdd/operator.py b/src/blenderbim/blenderbim/bim/module/bsdd/operator.py index 85299d81ff..129a332112 100644 --- a/src/blenderbim/blenderbim/bim/module/bsdd/operator.py +++ b/src/blenderbim/blenderbim/bim/module/bsdd/operator.py @@ -22,73 +22,39 @@ import bsdd import json import ifcopenshell import blenderbim.tool as tool +from blenderbim.core import bsdd as core +from blenderbim.tool.bsdd import Bsdd class LoadBSDDDomains(bpy.types.Operator): bl_idname = "bim.load_bsdd_domains" - bl_label = "Load bSDD Domains" + bl_label = "Load bSDD Dictionaries" bl_options = {"REGISTER", "UNDO"} def execute(self, context): - props = context.scene.BIMBSDDProperties - props.domains.clear() - client = bsdd.Client() - - if props.load_preview_domains: - domains = client.Domain() - else: - domains = [d for d in client.Domain() if d["status"] == "Active"] - - for domain in sorted(domains, key=lambda x: x["name"]): - new = props.domains.add() - new.name = domain["name"] - new.namespace_uri = domain["namespaceUri"] - new.default_language_code = domain["defaultLanguageCode"] - new.organization_name_owner = domain["organizationNameOwner"] - new.status = domain["status"] - new.version = domain["version"] + core.load_bsdd(context.scene.BIMBSDDProperties, bsdd.Client(), Bsdd) return {"FINISHED"} -class SetActiveBSDDDomain(bpy.types.Operator): +class SetActiveBSDDDictionary(bpy.types.Operator): bl_idname = "bim.set_active_bsdd_domain" - bl_label = "Load bSDD Domains" + bl_label = "Load bSDD Dictionary" bl_options = {"REGISTER", "UNDO"} name: bpy.props.StringProperty() uri: bpy.props.StringProperty() def execute(self, context): - props = context.scene.BIMBSDDProperties - props.active_domain = self.name - props.active_uri = self.uri + core.set_active_bsdd_dictionary(context, self.name, self.uri) return {"FINISHED"} -class SearchBSDDClassifications(bpy.types.Operator): +class SearchBSDDClass(bpy.types.Operator): bl_idname = "bim.search_bsdd_classifications" - bl_label = "Search bSDD Classifications" + bl_label = "Search bSDD Class" bl_options = {"REGISTER", "UNDO"} def execute(self, context): - props = context.scene.BIMBSDDProperties - props.classifications.clear() - client = bsdd.Client() - related_ifc_entities = [] - if len(props.keyword) < 3: - return {"FINISHED"} - if props.should_filter_ifc_class and context.active_object: - element = tool.Ifc.get_entity(context.active_object) - if element: - related_ifc_entities = [element.is_a()] - results = client.ClassificationSearchOpen(props.keyword, DomainNamespaceUris=[props.active_uri], RelatedIfcEntities=related_ifc_entities) - for result in sorted(results["classifications"], key=lambda x: x["referenceCode"]): - new = props.classifications.add() - new.name = result["name"] - new.reference_code = result["referenceCode"] - new.description = result.get("description", "") - new.namespace_uri = result["namespaceUri"] - new.domain_name = result["domainName"] - new.domain_namespace_uri = result["domainNamespaceUri"] + core.search_class(context, bsdd.Client(), tool.Bsdd, tool.Ifc) return {"FINISHED"} @@ -98,50 +64,5 @@ class GetBSDDClassificationProperties(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): - bprops = context.scene.BIMBSDDProperties - bprops.classification_psets.clear() - bsdd_classification = bprops.classifications[bprops.active_classification_index] - client = bsdd.Client() - data = client.Classification(bsdd_classification.namespace_uri) - - properties = data.get("classificationProperties", None) - if not properties: - return {"FINISHED"} - - psets = {} - - for prop in properties: - if prop.get("propertyDomainName") != "IFC": - continue - pset = prop.get("propertySet", None) - if not pset: - continue - psets.setdefault(pset, {}) - - predefined_value = prop.get("predefinedValue") - if predefined_value: - possible_values = [predefined_value] - else: - possible_values = prop.get("possibleValues", []) or [] - possible_values = [v["value"] for v in possible_values] - - psets[pset][prop["name"]] = {"data_type": prop["dataType"], "possible_values": possible_values} - - data_type_map = { - "String": "string", - "Real": "float", - "Boolean": "boolean", - } - - for pset_name, pset in psets.items(): - new = bprops.classification_psets.add() - new.name = pset_name - for name, data in pset.items(): - new2 = new.properties.add() - new2.name = name - if data["possible_values"]: - new2.enum_items = json.dumps(data["possible_values"]) - new2.data_type = "enum" - else: - new2.data_type = data_type_map[data["data_type"]] + core.get_class_properties(context, bsdd.Client(), tool.Bsdd) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/bsdd/prop.py b/src/blenderbim/blenderbim/bim/module/bsdd/prop.py index 6e9fdeab05..2a54c8f7ce 100644 --- a/src/blenderbim/blenderbim/bim/module/bsdd/prop.py +++ b/src/blenderbim/blenderbim/bim/module/bsdd/prop.py @@ -33,7 +33,7 @@ from bpy.props import ( class BSDDDomain(PropertyGroup): name: StringProperty(name="Name") - namespace_uri: StringProperty(name="URI") + uri: StringProperty(name="URI") default_language_code: StringProperty(name="Language") organization_name_owner: StringProperty(name="Organization") status: StringProperty(name="Status") @@ -44,7 +44,7 @@ class BSDDClassification(PropertyGroup): name: StringProperty(name="Name") reference_code: StringProperty(name="Reference Code") description: StringProperty(name="Description") - namespace_uri: StringProperty(name="Namespace URI") + uri: StringProperty(name="Namespace URI") domain_name: StringProperty(name="Domain Name") domain_namespace_uri: StringProperty(name="Domain Namespace URI") diff --git a/src/blenderbim/blenderbim/bim/module/bsdd/ui.py b/src/blenderbim/blenderbim/bim/module/bsdd/ui.py index 8bc9e487b4..899e8dcf2b 100644 --- a/src/blenderbim/blenderbim/bim/module/bsdd/ui.py +++ b/src/blenderbim/blenderbim/bim/module/bsdd/ui.py @@ -68,7 +68,7 @@ class BIM_UL_bsdd_domains(UIList): row.label(text=f"{item.name} ({item.organization_name_owner})") op = row.operator("bim.set_active_bsdd_domain", text="", icon="RESTRICT_SELECT_OFF") op.name = item.name - op.uri = item.namespace_uri + op.uri = item.uri class BIM_UL_bsdd_classifications(UIList): diff --git a/src/blenderbim/blenderbim/bim/module/classification/operator.py b/src/blenderbim/blenderbim/bim/module/classification/operator.py index b57d400271..607ba967f5 100644 --- a/src/blenderbim/blenderbim/bim/module/classification/operator.py +++ b/src/blenderbim/blenderbim/bim/module/classification/operator.py @@ -107,13 +107,13 @@ class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator): props = context.scene.BIMBSDDProperties domain = [d for d in props.domains if d.name == props.active_domain][0] for element in tool.Ifc.get().by_type("IfcClassification"): - if element.Name == props.active_domain or element.Location == domain.namespace_uri: + if element.Name == props.active_domain or element.Location == domain.uri: return classification = ifcopenshell.api.run( "classification.add_classification", tool.Ifc.get(), classification=props.active_domain ) classification.Source = domain.organization_name_owner - classification.Location = domain.namespace_uri + classification.Location = domain.uri classification.Edition = domain.version @@ -418,7 +418,7 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator): identification=bsdd_classification.reference_code, name=bsdd_classification.name, ) - reference.Location = bsdd_classification.namespace_uri + reference.Location = bsdd_classification.uri for classification_pset in bprops.classification_psets: is_pset = not classification_pset.name.startswith("Qto_") diff --git a/src/blenderbim/blenderbim/core/bsdd.py b/src/blenderbim/blenderbim/core/bsdd.py new file mode 100644 index 0000000000..17b922d205 --- /dev/null +++ b/src/blenderbim/blenderbim/core/bsdd.py @@ -0,0 +1,40 @@ +def load_bsdd(props, client, bsdd): + props.domains.clear() + if props.load_preview_domains: + dictionaries = bsdd.get_dictionaries(client) + else: + dictionaries = bsdd.get_dictionaries(client, "Active") + if not props.load_preview_domains: + dictionaries = filter(lambda d: d["status"] == "Active", dictionaries) + for dictionary in sorted(dictionaries, key=lambda d: d["name"]): + new = props.domains.add() + bsdd.fill_dictionary_prop(new, dictionary) + + +def set_active_bsdd_dictionary(context, name, uri): + props = context.scene.BIMBSDDProperties + props.active_domain = name + props.active_uri = uri + + +def search_class(context, client, bsdd, ifc): + props = context.scene.BIMBSDDProperties + props.classifications.clear() + if len(props.keyword) < 3: + return + related_entities = bsdd.get_related_ifc_entities(props.keyword, props.should_filter_ifc_class, + context.active_object, ifc) + response: dict = client.search_class(props.keyword, dictionary_uris=[props.active_uri], + related_ifc_entities=related_entities) + classes = response.get("classes", []) + for _class in sorted(classes, key=lambda c: c["referenceCode"]): + bsdd.fill_class_prop(props.classifications.add(), _class) + + +def get_class_properties(context, client, bsdd): + bprops = context.scene.BIMBSDDProperties + data = bsdd.get_active_class_data(bprops, client) + pset_dict = bsdd.get_property_dict(data) + if pset_dict is None: + return + bsdd.create_classification_psets(bprops, pset_dict) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 0a12e0b2ca..abcb5677bd 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -136,6 +136,20 @@ class Brick: def clear_breadcrumbs(cls, split_screen=False): pass +class Bsdd: + def get_dictionaries(cls, client): pass + + def fill_dictionary_prop(cls, prop, dictionary): pass + + def get_related_ifc_entities(cls, keyword, filter_ifc_class, active_object, ifc): pass + + def fill_class_prop(cls, prop, _class): pass + + def get_active_class_data(cls, prop, client): pass + + def get_property_dict(cls, class_data): pass + + def create_classification_psets(cls, props, pset_dic): pass @interface class Collector: def assign(cls, obj): pass diff --git a/src/blenderbim/blenderbim/tool/__init__.py b/src/blenderbim/blenderbim/tool/__init__.py index 3cc7507457..4f98a7b5e3 100644 --- a/src/blenderbim/blenderbim/tool/__init__.py +++ b/src/blenderbim/blenderbim/tool/__init__.py @@ -20,6 +20,7 @@ from blenderbim.tool.aggregate import Aggregate from blenderbim.tool.blender import Blender from blenderbim.tool.boundary import Boundary from blenderbim.tool.brick import Brick +from blenderbim.tool.bsdd import Bsdd from blenderbim.tool.cad import Cad from blenderbim.tool.collector import Collector from blenderbim.tool.context import Context diff --git a/src/blenderbim/blenderbim/tool/bsdd.py b/src/blenderbim/blenderbim/tool/bsdd.py new file mode 100644 index 0000000000..72685b606c --- /dev/null +++ b/src/blenderbim/blenderbim/tool/bsdd.py @@ -0,0 +1,91 @@ +import blenderbim.core.tool +import json + + +class Bsdd(blenderbim.core.tool.Bsdd): + @classmethod + def get_dictionaries(cls, client, status=None) -> list: + response = client.get_dictionary() + dicts = response.get("dictionaries") or [] + if status is not None: + dicts = filter(lambda d: d["status"] == status, dicts) + return dicts + + @classmethod + def fill_dictionary_prop(cls, prop, dictionary): + prop.name = dictionary["name"] + prop.uri = dictionary["uri"] + prop.default_language_code = dictionary["defaultLanguageCode"] + prop.organization_name_owner = dictionary["organizationNameOwner"] + prop.status = dictionary["status"] + prop.version = dictionary["version"] + + @classmethod + def get_related_ifc_entities(cls, keyword, filter_ifc_class: bool, active_object, ifc): + related_ifc_entities = [] + if len(keyword) < 3: + return {"FINISHED"} + if filter_ifc_class and active_object: + element = ifc.get_entity(active_object) + if element: + related_ifc_entities = [element.is_a()] + return related_ifc_entities + + @classmethod + def fill_class_prop(cls, prop, _class: dict): + prop.name = _class["name"] + prop.reference_code = _class["referenceCode"] + prop.description = _class.get("description", "") + prop.uri = _class["uri"] + prop.domain_name = _class["dictionaryName"] + prop.domain_namespace_uri = _class["dictionaryUri"] + + @classmethod + def get_active_class_data(cls, prop, client): + prop.classification_psets.clear() + bsdd_classification = prop.classifications[prop.active_classification_index] + return client.get_class(bsdd_classification.uri) + + @classmethod + def get_property_dict(cls, class_data: dict): + properties = class_data.get("classProperties", None) + if not properties: + return None + + psets = {} + for prop in properties: + if prop.get("propertyDictionaryName") == "IFC": + continue + pset = prop.get("propertySet", None) + if not pset: + continue + psets.setdefault(pset, {}) + + predefined_value = prop.get("predefinedValue") + if predefined_value: + possible_values = [predefined_value] + else: + possible_values = prop.get("allowedValues", []) or [] + possible_values = [v["value"] for v in possible_values] + + psets[pset][prop["name"]] = {"data_type": prop["dataType"], "possible_values": possible_values} + return psets + + @classmethod + def create_classification_psets(cls, props, pset_dict: dict): + data_type_map = { + "String": "string", + "Real": "float", + "Boolean": "boolean", + } + for pset_name, pset in pset_dict.items(): + new = props.classification_psets.add() + new.name = pset_name + for name, data in pset.items(): + new2 = new.properties.add() + new2.name = name + if data["possible_values"]: + new2.enum_items = json.dumps(data["possible_values"]) + new2.data_type = "enum" + else: + new2.data_type = data_type_map[data["data_type"]]