Organisations now support null attributes.

This commit is contained in:
Dion Moult
2021-09-29 18:39:10 +10:00
parent 406fd158d3
commit beff90cd5e
16 changed files with 454 additions and 315 deletions
@@ -20,7 +20,6 @@ import bpy
from . import ui, prop, operator
classes = (
operator.AddOrRemoveElementFromCollection,
operator.EnableEditingPerson,
operator.DisableEditingPerson,
operator.AddPerson,
@@ -45,8 +44,6 @@ classes = (
operator.RemoveAddressAttribute,
operator.EditAddress,
operator.RemoveAddress,
prop.Person,
prop.Organisation,
prop.BIMOwnerProperties,
ui.BIM_PT_people,
ui.BIM_PT_organisations,
@@ -20,7 +20,60 @@ import bpy
import blenderbim.tool as tool
class PeopleData:
class RolesAddressesData:
@classmethod
def get_roles(cls, parent):
results = []
for role in parent.Roles or []:
results.append(
{
"id": role.id(),
"is_editing": bpy.context.scene.BIMOwnerProperties.active_role_id == role.id(),
"label": role.UserDefinedRole or role.Role,
"props": bpy.context.scene.BIMOwnerProperties.role_attributes,
}
)
return results
@classmethod
def get_addresses(cls, parent):
results = []
for address in parent.Addresses or []:
results.append(
{
"id": address.id(),
"is_editing": bpy.context.scene.BIMOwnerProperties.active_address_id == address.id(),
"label": address.is_a(),
"props": bpy.context.scene.BIMOwnerProperties.address_attributes,
"list_attributes": cls.get_address_list_attributes(address),
}
)
return results
@classmethod
def get_address_list_attributes(cls, address):
results = []
props = bpy.context.scene.BIMOwnerProperties
if address.is_a("IfcPostalAddress"):
names = ["AddressLines"]
elif address.is_a("IfcTelecomAddress"):
names = ["TelephoneNumbers", "FacsimileNumbers", "ElectronicMailAddresses", "MessagingIDs"]
for name in names:
if name == "AddressLines":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.address_lines)]
elif name == "TelephoneNumbers":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.telephone_numbers)]
elif name == "FacsimileNumbers":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.facsimile_numbers)]
elif name == "ElectronicMailAddresses":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.electronic_mail_addresses)]
elif name == "MessagingIDs":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.messaging_ids)]
results.append({"name": name, "items": items})
return results
class PeopleData(RolesAddressesData):
data = {}
is_loaded = False
@@ -41,8 +94,8 @@ class PeopleData:
"is_editing": cls.get_person_is_editing(person),
"is_engaged": bool(person.EngagedIn),
"list_attributes": cls.get_person_list_attributes(person),
"roles": cls.get_person_roles(person),
"addresses": cls.get_person_addresses(person),
"roles": cls.get_roles(person),
"addresses": cls.get_addresses(person),
}
)
return people
@@ -77,53 +130,29 @@ class PeopleData:
results.append({"name": name, "items": items})
return results
@classmethod
def get_person_roles(cls, person):
results = []
for role in person.Roles or []:
results.append(
{
"id": role.id(),
"is_editing": bpy.context.scene.BIMOwnerProperties.active_role_id == role.id(),
"label": role.UserDefinedRole or role.Role,
"props": bpy.context.scene.BIMOwnerProperties.role_attributes,
}
)
return results
class OrganisationsData(RolesAddressesData):
data = {}
is_loaded = False
@classmethod
def get_person_addresses(cls, person):
results = []
for address in person.Addresses or []:
results.append(
{
"id": address.id(),
"is_editing": bpy.context.scene.BIMOwnerProperties.active_address_id == address.id(),
"label": address.is_a(),
"props": bpy.context.scene.BIMOwnerProperties.address_attributes,
"list_attributes": cls.get_address_list_attributes(address),
}
)
return results
def load(cls):
cls.data = {"organisations": cls.get_organisations()}
cls.is_loaded = True
@classmethod
def get_address_list_attributes(cls, address):
results = []
props = bpy.context.scene.BIMOwnerProperties
if address.is_a("IfcPostalAddress"):
names = ["AddressLines"]
elif address.is_a("IfcTelecomAddress"):
names = ["TelephoneNumbers", "FacsimileNumbers", "ElectronicMailAddresses", "MessagingIDs"]
for name in names:
if name == "AddressLines":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.address_lines)]
elif name == "TelephoneNumbers":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.telephone_numbers)]
elif name == "FacsimileNumbers":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.facsimile_numbers)]
elif name == "ElectronicMailAddresses":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.electronic_mail_addresses)]
elif name == "MessagingIDs":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.messaging_ids)]
results.append({"name": name, "items": items})
return results
def get_organisations(cls):
organisations = []
for organisation in tool.Ifc().get().by_type("IfcOrganization"):
organisations.append(
{
"id": organisation.id(),
"props": bpy.context.scene.BIMOwnerProperties.organisation_attributes,
"name": organisation.Name,
"is_editing": bpy.context.scene.BIMOwnerProperties.active_organisation_id == organisation.id(),
"is_engaged": bool(organisation.Engages),
"roles": cls.get_roles(organisation),
"addresses": cls.get_addresses(organisation),
}
)
return organisations
@@ -30,43 +30,7 @@ class Operator:
def execute(self, context):
IfcStore.execute_ifc_operator(self, context)
blenderbim.bim.module.owner.data.PeopleData.is_loaded = False
return {"FINISHED"}
def flatten_collection(collection):
return [v.name for v in collection] if collection else None
def populate_collection(collection, collection_data):
collection.clear()
if collection_data:
for value in collection_data:
collection.add().name = value
else:
collection.add()
class AddOrRemoveElementFromCollection(bpy.types.Operator):
bl_idname = "bim.add_or_remove_element_from_collection"
bl_label = "Add or Remove Element From Collection"
bl_options = {"REGISTER", "UNDO"}
operation: bpy.props.EnumProperty(
items=(("+", "Add", "Add item to collection"), ("-", "Remove", "Remove item from collection")),
default="+",
)
collection_path: bpy.props.StringProperty()
selected_item_idx: bpy.props.IntProperty(default=-1)
def execute(self, context):
# Ugly but I hate using eval()
collection = context.scene
for attr in self.collection_path.split("."):
if hasattr(collection, attr):
collection = getattr(collection, attr)
if self.operation == "+" and hasattr(collection, "add"):
collection.add()
elif hasattr(collection, "remove") and 0 <= self.selected_item_idx < len(collection):
collection.remove(self.selected_item_idx)
blenderbim.bim.module.owner.data.OrganisationsData.is_loaded = False
return {"FINISHED"}
@@ -256,90 +220,50 @@ class RemoveAddress(bpy.types.Operator, Operator):
core.remove_address(tool.Ifc(), address=tool.Ifc().get().by_id(self.address))
class EnableEditingOrganisation(bpy.types.Operator):
class EnableEditingOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_organisation"
bl_label = "Enable Editing Organisation"
bl_options = {"REGISTER", "UNDO"}
organisation_id: bpy.props.IntProperty()
organisation: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMOwnerProperties
props.active_organisation_id = self.organisation_id
data = Data.organisations[self.organisation_id]
identification = data["Id"] if self.file.schema == "IFC2X3" else data["Identification"]
props.organisation.identification = identification or ""
props.organisation.name = data["Name"]
props.organisation.description = data["Description"] or ""
return {"FINISHED"}
def _execute(self, context):
core.enable_editing_organisation(
tool.OrganisationEditor, organisation=tool.Ifc().get().by_id(self.organisation)
)
class DisableEditingOrganisation(bpy.types.Operator):
class DisableEditingOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_organisation"
bl_label = "Disable Editing Organisation"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMOwnerProperties.active_organisation_id = 0
return {"FINISHED"}
def _execute(self, context):
core.disable_editing_organisation(tool.OrganisationEditor)
class AddOrganisation(bpy.types.Operator):
class AddOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.add_organisation"
bl_label = "Add Organisation"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
ifcopenshell.api.run("owner.add_organisation", IfcStore.get_file())
Data.load(IfcStore.get_file())
return {"FINISHED"}
core.add_organisation(tool.Ifc)
class EditOrganisation(bpy.types.Operator):
class EditOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.edit_organisation"
bl_label = "Edit Organisation"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMOwnerProperties
attributes = {
"Identification": props.organisation.identification or None,
"Name": props.organisation.name,
"Description": props.organisation.description or None,
}
if self.file.schema == "IFC2X3":
attributes["Id"] = attributes["Identification"]
del attributes["Identification"]
ifcopenshell.api.run(
"owner.edit_organisation",
self.file,
**{"organisation": self.file.by_id(props.active_organisation_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_organisation()
return {"FINISHED"}
core.edit_organisation(tool.Ifc, tool.OrganisationEditor)
class RemoveOrganisation(bpy.types.Operator):
class RemoveOrganisation(bpy.types.Operator, Operator):
bl_idname = "bim.remove_organisation"
bl_label = "Remove Organisation"
bl_options = {"REGISTER", "UNDO"}
organisation_id: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
organisation: bpy.props.IntProperty()
def _execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"owner.remove_organisation", self.file, **{"organisation": self.file.by_id(self.organisation_id)}
)
Data.load(IfcStore.get_file())
return {"FINISHED"}
core.remove_organisation(tool.Ifc, tool.Ifc.get().by_id(self.organisation))
@@ -67,30 +67,14 @@ def getOrganisations(self, context):
return _organisations_enum
class Organisation(PropertyGroup):
identification: StringProperty(name="Identification")
name: StringProperty(name="Name")
description: StringProperty(name="Description")
class Person(PropertyGroup):
name: StringProperty(name="Identification")
family_name: StringProperty(name="Family Name")
given_name: StringProperty(name="Given Name")
middle_names: CollectionProperty(type=StrProperty, name="Middle Names")
prefix_titles: CollectionProperty(type=StrProperty, name="Prefixes")
suffix_titles: CollectionProperty(type=StrProperty, name="Suffixes")
class BIMOwnerProperties(PropertyGroup):
person: PointerProperty(type=Person)
person_attributes: CollectionProperty(name="Person Attributes", type=Attribute)
middle_names: CollectionProperty(type=StrProperty, name="Middle Names")
prefix_titles: CollectionProperty(type=StrProperty, name="Prefixes")
suffix_titles: CollectionProperty(type=StrProperty, name="Suffixes")
active_person_id: IntProperty(name="Active Person Id")
organisation: PointerProperty(type=Organisation)
active_organisation_id: IntProperty(name="Active Organisation Id")
organisation_attributes: CollectionProperty(name="Organisation Attributes", type=Attribute)
active_role_id: IntProperty(name="Active Role Id")
role_attributes: CollectionProperty(name="Role Attributes", type=Attribute)
active_address_id: IntProperty(name="Active Address Id")
+60 -150
View File
@@ -19,103 +19,70 @@
import bpy
import blenderbim.bim.helper
import blenderbim.tool as tool
from blenderbim.bim.module.owner.data import PeopleData
from blenderbim.bim.module.owner.data import PeopleData, OrganisationsData
from bpy.types import Panel
from ifcopenshell.api.owner.data import Data
from blenderbim.bim.ifc import IfcStore
def draw_string_collection(layout, owner, collection_name):
column = layout.column(align=True)
collection = getattr(owner, collection_name)
for i in range(len(collection)):
if i == 0:
row = draw_prop_on_new_row(
column, collection[i], "name", align=True, text=f"{owner.bl_rna.properties[collection_name].name}"
)
add_op = row.operator("bim.add_or_remove_element_from_collection", icon="ADD", text="")
add_op.operation = "+"
add_op.collection_path = collection.path_from_id()
else:
row = draw_prop_on_new_row(column, collection[i], "name", align=True, text=f"#{i + 1}")
rem_op = row.operator("bim.add_or_remove_element_from_collection", icon="REMOVE", text="")
rem_op.operation = "-"
rem_op.collection_path = collection.path_from_id()
rem_op.selected_item_idx = i
def draw_prop_on_new_row(layout, owner, attribute, align=False, **kwargs):
row = layout.row(align=align)
row.prop(owner, attribute, **kwargs)
return row
def draw_roles_ui(box, assigned_object_id, roles, context):
props = context.scene.BIMOwnerProperties
def draw_roles(box, parent):
row = box.row(align=True)
row.label(text="Roles")
row.operator("bim.add_role", icon="ADD", text="").assigned_object_id = assigned_object_id
for role_id in roles:
role = Data.roles[role_id]
if props.active_role_id == role_id:
blender_role = props.role
box2 = box.box()
row = draw_prop_on_new_row(box2, blender_role, "name", align=True, icon="MOD_CLOTH", text="")
row.operator("bim.edit_role", icon="CHECKMARK", text="")
op = row.operator("bim.add_role", icon="ADD", text="")
op.parent = parent["id"]
for role in parent["roles"]:
if role["is_editing"]:
row = box.row(align=True)
row.operator("bim.edit_role", icon="CHECKMARK")
row.operator("bim.disable_editing_role", icon="CANCEL", text="")
if blender_role.name == "USERDEFINED":
draw_prop_on_new_row(box2, blender_role, "user_defined_role")
draw_prop_on_new_row(box2, blender_role, "description")
blenderbim.bim.helper.draw_attributes(role["props"], box)
else:
row = box.row(align=True)
row.label(text=role["UserDefinedRole"] or role["Role"])
row.operator("bim.enable_editing_role", icon="GREASEPENCIL", text="").role_id = role_id
row.operator("bim.remove_role", icon="X", text="").role_id = role_id
row.label(text=role["label"])
row.operator("bim.enable_editing_role", icon="GREASEPENCIL", text="").role = role["id"]
row.operator("bim.remove_role", icon="X", text="").role = role["id"]
def draw_addresses_ui(box, assigned_object_id, addresses, file, context):
props = context.scene.BIMOwnerProperties
def draw_addresses(box, parent):
row = box.row(align=True)
row.label(text="Addresses")
op = row.operator("bim.add_address", icon="LINK_BLEND", text="")
op.assigned_object_id = assigned_object_id
op.parent = parent["id"]
op.ifc_class = "IfcTelecomAddress"
op = row.operator("bim.add_address", icon="APPEND_BLEND", text="")
op.assigned_object_id = assigned_object_id
op.parent = parent["id"]
op.ifc_class = "IfcPostalAddress"
for address_id in addresses:
address = Data.addresses[address_id]
if props.active_address_id == address_id:
blender_address = props.address
box2 = box.box()
row = draw_prop_on_new_row(box2, blender_address, "purpose", align=True, icon="MOD_CLOTH", text="")
row.operator("bim.edit_address", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_address", icon="CANCEL", text="")
if blender_address.purpose == "USERDEFINED":
draw_prop_on_new_row(box2, blender_address, "user_defined_purpose")
draw_prop_on_new_row(box2, blender_address, "description")
if address["type"] == "IfcTelecomAddress":
draw_string_collection(box2, blender_address, "telephone_numbers")
draw_string_collection(box2, blender_address, "facsimile_numbers")
draw_prop_on_new_row(box2, blender_address, "pager_number")
draw_string_collection(box2, blender_address, "electronic_mail_addresses")
draw_prop_on_new_row(box2, blender_address, "www_home_page_url")
if file.schema != "IFC2X3":
draw_string_collection(box2, blender_address, "messaging_ids")
elif address["type"] == "IfcPostalAddress":
draw_prop_on_new_row(box2, blender_address, "internal_location")
draw_string_collection(box2, blender_address, "address_lines")
draw_prop_on_new_row(box2, blender_address, "postal_box")
draw_prop_on_new_row(box2, blender_address, "town")
draw_prop_on_new_row(box2, blender_address, "region")
draw_prop_on_new_row(box2, blender_address, "postal_code")
draw_prop_on_new_row(box2, blender_address, "country")
for address in parent["addresses"]:
if address["is_editing"]:
row = box.row(align=True)
row.operator("bim.edit_address", icon="CHECKMARK")
row.operator("bim.disable_editing_address", icon="CANCEL", text="")
blenderbim.bim.helper.draw_attributes(address["props"], box)
for attribute in address["list_attributes"]:
row = box.row(align=True)
row.label(text=attribute["name"])
op = row.operator("bim.add_address_attribute", icon="ADD", text="")
op.name = attribute["name"]
for item in attribute["items"]:
row = box.row(align=True)
row.prop(item["prop"], "name", text="")
op = row.operator("bim.remove_address_attribute", icon="REMOVE", text="")
op.name = attribute["name"]
op.id = item["id"]
else:
row = box.row(align=True)
row.label(text=address["type"])
row.operator("bim.enable_editing_address", icon="GREASEPENCIL", text="").address_id = address_id
row.operator("bim.remove_address", icon="X", text="").address_id = address_id
row.label(text=address["label"])
row.operator("bim.enable_editing_address", icon="GREASEPENCIL", text="").address = address["id"]
row.operator("bim.remove_address", icon="X", text="").address = address["id"]
class BIM_PT_people(bpy.types.Panel):
@@ -164,8 +131,8 @@ class BIM_PT_people(bpy.types.Panel):
op.name = attribute["name"]
op.id = item["id"]
self.draw_roles(box, person)
self.draw_addresses(box, person)
draw_roles(box, person)
draw_addresses(box, person)
else:
row = self.layout.row(align=True)
row.label(text=person["name"])
@@ -173,58 +140,6 @@ class BIM_PT_people(bpy.types.Panel):
if not person["is_engaged"]:
row.operator("bim.remove_person", icon="X", text="").person = person["id"]
def draw_roles(self, box, person):
row = box.row(align=True)
row.label(text="Roles")
op = row.operator("bim.add_role", icon="ADD", text="")
op.parent = person["id"]
for role in person["roles"]:
if role["is_editing"]:
row = box.row(align=True)
row.operator("bim.edit_role", icon="CHECKMARK")
row.operator("bim.disable_editing_role", icon="CANCEL", text="")
blenderbim.bim.helper.draw_attributes(role["props"], box)
else:
row = box.row(align=True)
row.label(text=role["label"])
row.operator("bim.enable_editing_role", icon="GREASEPENCIL", text="").role = role["id"]
row.operator("bim.remove_role", icon="X", text="").role = role["id"]
def draw_addresses(self, box, person):
row = box.row(align=True)
row.label(text="Addresses")
op = row.operator("bim.add_address", icon="LINK_BLEND", text="")
op.parent = person["id"]
op.ifc_class = "IfcTelecomAddress"
op = row.operator("bim.add_address", icon="APPEND_BLEND", text="")
op.parent = person["id"]
op.ifc_class = "IfcPostalAddress"
for address in person["addresses"]:
if address["is_editing"]:
row = box.row(align=True)
row.operator("bim.edit_address", icon="CHECKMARK")
row.operator("bim.disable_editing_address", icon="CANCEL", text="")
blenderbim.bim.helper.draw_attributes(address["props"], box)
for attribute in address["list_attributes"]:
row = box.row(align=True)
row.label(text=attribute["name"])
op = row.operator("bim.add_address_attribute", icon="ADD", text="")
op.name = attribute["name"]
for item in attribute["items"]:
row = box.row(align=True)
row.prop(item["prop"], "name", text="")
op = row.operator("bim.remove_address_attribute", icon="REMOVE", text="")
op.name = attribute["name"]
op.id = item["id"]
else:
row = box.row(align=True)
row.label(text=address["label"])
row.operator("bim.enable_editing_address", icon="GREASEPENCIL", text="").address = address["id"]
row.operator("bim.remove_address", icon="X", text="").address = address["id"]
class BIM_PT_organisations(Panel):
bl_label = "IFC Organisations"
@@ -239,40 +154,35 @@ class BIM_PT_organisations(Panel):
return IfcStore.get_file()
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not OrganisationsData.is_loaded:
OrganisationsData.load()
self.file = IfcStore.get_file()
self.layout.use_property_split = True
self.layout.use_property_decorate = False
props = context.scene.BIMOwnerProperties
row = self.layout.row()
row.operator("bim.add_organisation", icon="ADD")
for organisation_id, organisation in Data.organisations.items():
if props.active_organisation_id == organisation_id:
blender_organisation = props.organisation
box = self.layout.box()
row = box.row(align=True)
row.prop(blender_organisation, "name", icon="USER", text="")
row.operator("bim.edit_organisation", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_organisation", icon="CANCEL", text="")
draw_prop_on_new_row(box, blender_organisation, "identification")
draw_prop_on_new_row(box, blender_organisation, "description")
for organisation in OrganisationsData.data["organisations"]:
self.draw_organisation(organisation)
draw_roles_ui(box, organisation_id, organisation["Roles"], context)
draw_addresses_ui(box, organisation_id, organisation["Addresses"], self.file, context)
else:
row = self.layout.row(align=True)
row.label(text=organisation["Name"])
if organisation["Roles"]:
row.label(text=", ".join([Data.roles[r]["Role"] for r in organisation["Roles"]]))
row.operator(
"bim.enable_editing_organisation", icon="GREASEPENCIL", text=""
).organisation_id = organisation_id
if not organisation["is_engaged"]:
row.operator("bim.remove_organisation", icon="X", text="").organisation_id = organisation_id
def draw_organisation(self, organisation):
if organisation["is_editing"]:
box = self.layout.box()
row = box.row(align=True)
row.operator("bim.edit_organisation", icon="CHECKMARK")
row.operator("bim.disable_editing_organisation", icon="CANCEL", text="")
blenderbim.bim.helper.draw_attributes(organisation["props"], box)
draw_roles(box, organisation)
draw_addresses(box, organisation)
else:
row = self.layout.row(align=True)
row.label(text=organisation["name"])
op = row.operator("bim.enable_editing_organisation", icon="GREASEPENCIL", text="")
op.organisation = organisation["id"]
if not organisation["is_engaged"]:
row.operator("bim.remove_organisation", icon="X", text="").organisation = organisation["id"]
class BIM_PT_owner(Panel):
+23
View File
@@ -98,3 +98,26 @@ def add_address_attribute(address_editor, name=None):
def remove_address_attribute(address_editor, name=None, id=None):
address_editor.remove_attribute(name, id)
def add_organisation(ifc):
return ifc.run("owner.add_organisation")
def remove_organisation(ifc, organisation=None):
ifc.run("owner.remove_organisation", organisation=organisation)
def enable_editing_organisation(organisation_editor, organisation=None):
organisation_editor.set_organisation(organisation)
organisation_editor.import_attributes()
def disable_editing_organisation(organisation_editor):
organisation_editor.clear_organisation()
def edit_organisation(ifc, organisation_editor):
organisation = organisation_editor.get_organisation()
ifc.run("owner.edit_organisation", organisation=organisation, attributes=organisation_editor.export_attributes())
organisation_editor.clear_organisation()
@@ -21,3 +21,4 @@ from blenderbim.core.tool.blender import Blender
from blenderbim.core.tool.person_editor import PersonEditor
from blenderbim.core.tool.role_editor import RoleEditor
from blenderbim.core.tool.address_editor import AddressEditor
from blenderbim.core.tool.organisation_editor import OrganisationEditor
@@ -0,0 +1,46 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import abc
class OrganisationEditor(abc.ABC):
@classmethod
@abc.abstractmethod
def set_organisation(cls, organisation):
pass
@classmethod
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_organisation(cls):
pass
@classmethod
@abc.abstractmethod
def get_organisation(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
@@ -21,3 +21,4 @@ from blenderbim.tool.blender import Blender
from blenderbim.tool.person_editor import PersonEditor
from blenderbim.tool.role_editor import RoleEditor
from blenderbim.tool.address_editor import AddressEditor
from blenderbim.tool.organisation_editor import OrganisationEditor
@@ -0,0 +1,53 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell.api
import blenderbim.core.tool
import blenderbim.bim.helper
import blenderbim.tool as tool
class OrganisationEditor(blenderbim.core.tool.organisation_editor.OrganisationEditor):
@classmethod
def set_organisation(cls, organisation):
bpy.context.scene.BIMOwnerProperties.active_organisation_id = organisation.id()
@classmethod
def import_attributes(cls):
organisation = tool.Ifc.get().by_id(bpy.context.scene.BIMOwnerProperties.active_organisation_id)
props = bpy.context.scene.BIMOwnerProperties
props.organisation_attributes.clear()
blenderbim.bim.helper.import_attributes(
"IfcOrganization", props.organisation_attributes, organisation.get_info()
)
@classmethod
def clear_organisation(cls):
bpy.context.scene.BIMOwnerProperties.active_organisation_id = 0
@classmethod
def export_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.organisation_attributes)
return attributes
@classmethod
def get_organisation(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_organisation_id)
+2 -1
View File
@@ -95,6 +95,7 @@ def additionally_the_object_name_is_selected(name):
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
def i_deselect_all_objects():
bpy.context.view_layer.objects.active = None
bpy.ops.object.select_all(action="DESELECT")
@@ -346,7 +347,7 @@ definitions = {
'I add a cube of size "([0-9]+)" at "(.*)"': i_add_a_cube_of_size_size_at_location,
'the object "(.*)" is selected': the_object_name_is_selected,
'additionally the object "(.*)" is selected': additionally_the_object_name_is_selected,
'I deselect all objects': i_deselect_all_objects,
"I deselect all objects": i_deselect_all_objects,
'I am on frame "([0-9]+)"': i_am_on_frame_number,
'I set "(.*)" to "(.*)"': i_set_prop_to_value,
'"(.*)" is "(.*)"': prop_is_value,
@@ -165,3 +165,38 @@ Scenario: Edit address
And I press "bim.enable_editing_address(address={address})"
And I press "bim.edit_address()"
Then nothing happens
Scenario: Add organisation
Given an empty IFC project
When I press "bim.add_organisation"
Then nothing happens
Scenario: Enable editing organisation
Given an empty IFC project
When I press "bim.add_organisation"
And the variable "organisation" is "{ifc}.by_type('IfcOrganization')[0].id()"
And I press "bim.enable_editing_organisation(organisation={organisation})"
Then "scene.BIMOwnerProperties.active_organisation_id" is "{organisation}"
Scenario: Disable editing organisation
Given an empty IFC project
When I press "bim.add_organisation"
And the variable "organisation" is "{ifc}.by_type('IfcOrganization')[0].id()"
And I press "bim.enable_editing_organisation(organisation={organisation})"
And I press "bim.disable_editing_organisation"
Then "scene.BIMOwnerProperties.active_organisation_id" is "0"
Scenario: Edit organisation
Given an empty IFC project
When I press "bim.add_organisation"
And the variable "organisation" is "{ifc}.by_type('IfcOrganization')[0].id()"
And I press "bim.enable_editing_organisation(organisation={organisation})"
And I press "bim.edit_organisation"
Then "scene.BIMOwnerProperties.active_organisation_id" is "0"
Scenario: Remove organisation
Given an empty IFC project
When I press "bim.add_organisation"
And the variable "organisation" is "{ifc}.by_type('IfcOrganization')[0].id()"
And I press "bim.remove_organisation(organisation={organisation})"
Then nothing happens
@@ -382,7 +382,7 @@ class TestRemoveFilling(test.bim.bootstrap.NewFile):
And I delete the selected objects
Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement"
And the void "IfcOpeningElement/Cube" is not filled by "Cube"
"""
"""
@test.bim.bootstrap.scenario
def test_removing_a_filling_using_deletion_on_the_opening(self):
+7
View File
@@ -56,6 +56,13 @@ def address_editor():
prophet.verify()
@pytest.fixture
def organisation_editor():
prophet = Prophecy(blenderbim.core.tool.OrganisationEditor)
yield prophet
prophet.verify()
class Prophecy:
def __init__(self, cls):
self.subject = cls
+35 -1
View File
@@ -18,7 +18,7 @@
import blenderbim.core.owner as subject
from test.core.bootstrap import ifc, blender, person_editor, role_editor, address_editor
from test.core.bootstrap import ifc, blender, person_editor, role_editor, address_editor, organisation_editor
class TestAddPerson:
@@ -147,3 +147,37 @@ class TestRemoveAddressAttribute:
def test_run(self, address_editor):
address_editor.remove_attribute("name", "id").should_be_called()
subject.remove_address_attribute(address_editor, name="name", id="id")
class TestAddOrganisation:
def test_run(self, ifc):
ifc.run("owner.add_organisation").should_be_called().will_return("organisation")
assert subject.add_organisation(ifc) == "organisation"
class TestRemoveOrganisation:
def test_run(self, ifc):
ifc.run("owner.remove_organisation", organisation="organisation").should_be_called()
subject.remove_organisation(ifc, organisation="organisation")
class TestEnableEditingOrganisation:
def test_run(self, organisation_editor):
organisation_editor.set_organisation("organisation").should_be_called()
organisation_editor.import_attributes().should_be_called()
subject.enable_editing_organisation(organisation_editor, organisation="organisation")
class TestDisableEditingOrganisation:
def test_run(self, organisation_editor):
organisation_editor.clear_organisation().should_be_called()
subject.disable_editing_organisation(organisation_editor)
class TestEditOrganisation:
def test_run(self, ifc, organisation_editor):
organisation_editor.get_organisation().should_be_called().will_return("organisation")
organisation_editor.export_attributes().should_be_called().will_return("attributes")
ifc.run("owner.edit_organisation", organisation="organisation", attributes="attributes").should_be_called()
organisation_editor.clear_organisation().should_be_called()
subject.edit_organisation(ifc, organisation_editor)
@@ -0,0 +1,94 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import test.bim.bootstrap
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.tool.organisation_editor import OrganisationEditor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.organisation_editor.OrganisationEditor)
class TestSetOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
organisation = ifcopenshell.file().createIfcOrganization()
subject().set_organisation(organisation)
assert bpy.context.scene.BIMOwnerProperties.active_organisation_id == organisation.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
organisation.Identification = "Identification"
organisation.Name = "Name"
organisation.Description = "Description"
subject().set_organisation(organisation)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.organisation_attributes.get("Identification").string_value == "Identification"
assert props.organisation_attributes.get("Name").string_value == "Name"
assert props.organisation_attributes.get("Description").string_value == "Description"
def test_overwriting_a_previous_import(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
organisation.Identification = "Identification"
organisation.Description = "Description"
subject().set_organisation(organisation)
subject().import_attributes()
organisation.Identification = "Identification2"
organisation.Description = None
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.organisation_attributes.get("Identification").string_value == "Identification2"
assert props.organisation_attributes.get("Description").string_value == ""
class TestClearOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
props = bpy.context.scene.BIMOwnerProperties
props.active_organisation_id = 1
subject().clear_organisation()
assert props.active_organisation_id == 0
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
TestImportAttributes().test_run()
assert subject().export_attributes() == {
"Identification": "Identification",
"Name": "Name",
"Description": "Description",
}
class TestGetOrganisation(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
organisation = ifc.createIfcOrganization()
subject().set_organisation(organisation)
assert subject().get_organisation() == organisation