Add support for null attributes for people, roles, and addresses. New prototoype testable architecture. See #1711.

This commit is contained in:
Dion Moult
2021-09-29 14:14:10 +10:00
parent 6fb4714317
commit 086d8749ec
31 changed files with 1975 additions and 361 deletions
+5
View File
@@ -436,6 +436,7 @@ endif
.PHONY: test .PHONY: test
test: test:
make test-core make test-core
make test-tool
make test-bim make test-bim
.PHONY: test-core .PHONY: test-core
@@ -446,6 +447,10 @@ test-core:
test-bim: test-bim:
pytest test/bim pytest test/bim
.PHONY: test-tool
test-tool:
pytest test/tool
.PHONY: qa .PHONY: qa
qa: qa:
black . black .
@@ -26,6 +26,8 @@ classes = (
operator.AddPerson, operator.AddPerson,
operator.EditPerson, operator.EditPerson,
operator.RemovePerson, operator.RemovePerson,
operator.AddPersonAttribute,
operator.RemovePersonAttribute,
operator.EnableEditingOrganisation, operator.EnableEditingOrganisation,
operator.DisableEditingOrganisation, operator.DisableEditingOrganisation,
operator.AddOrganisation, operator.AddOrganisation,
@@ -39,10 +41,10 @@ classes = (
operator.EnableEditingAddress, operator.EnableEditingAddress,
operator.DisableEditingAddress, operator.DisableEditingAddress,
operator.AddAddress, operator.AddAddress,
operator.AddAddressAttribute,
operator.RemoveAddressAttribute,
operator.EditAddress, operator.EditAddress,
operator.RemoveAddress, operator.RemoveAddress,
prop.Role,
prop.Address,
prop.Person, prop.Person,
prop.Organisation, prop.Organisation,
prop.BIMOwnerProperties, prop.BIMOwnerProperties,
@@ -0,0 +1,129 @@
# 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 blenderbim.tool as tool
class PeopleData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"people": cls.get_people()}
cls.is_loaded = True
@classmethod
def get_people(cls):
people = []
for person in tool.Ifc().get().by_type("IfcPerson"):
people.append(
{
"id": person.id(),
"props": bpy.context.scene.BIMOwnerProperties.person_attributes,
"name": cls.get_person_name(person),
"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),
}
)
return people
@classmethod
def get_person_name(cls, person):
if tool.Ifc().get_schema() == "IFC2X3":
name = person.Id
else:
name = person.Identification
name = name or "*"
if person.GivenName or person.FamilyName:
full_name = "{} {}".format(person.GivenName or "", person.FamilyName or "").strip()
name += f" ({full_name})"
return name
@classmethod
def get_person_is_editing(cls, person):
return bpy.context.scene.BIMOwnerProperties.active_person_id == person.id()
@classmethod
def get_person_list_attributes(cls, person):
results = []
props = bpy.context.scene.BIMOwnerProperties
for name in ["MiddleNames", "PrefixTitles", "SuffixTitles"]:
if name == "MiddleNames":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.middle_names)]
elif name == "PrefixTitles":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.prefix_titles)]
elif name == "SuffixTitles":
items = [{"id": id, "prop": prop} for id, prop in enumerate(props.suffix_titles)]
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
@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
@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
@@ -18,10 +18,21 @@
import bpy import bpy
import ifcopenshell.api import ifcopenshell.api
import blenderbim.tool as tool
import blenderbim.core.owner as core
import blenderbim.bim.module.owner.data
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.owner.data import Data from ifcopenshell.api.owner.data import Data
# TODO: Just testing
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): def flatten_collection(collection):
return [v.name for v in collection] if collection else None return [v.name for v in collection] if collection else None
@@ -59,317 +70,190 @@ class AddOrRemoveElementFromCollection(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class EnableEditingPerson(bpy.types.Operator): class EnableEditingPerson(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_person" bl_idname = "bim.enable_editing_person"
bl_label = "Enable Editing Person" bl_label = "Enable Editing Person"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
person_id: bpy.props.IntProperty() person: bpy.props.IntProperty()
def execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.enable_editing_person(tool.PersonEditor(), person=tool.Ifc().get().by_id(self.person))
props = context.scene.BIMOwnerProperties
props.active_person_id = self.person_id
data = Data.people[self.person_id]
name = data["Id"] if self.file.schema == "IFC2X3" else data["Identification"]
person = props.person
person.name = name or ""
person.family_name = data["FamilyName"] or ""
person.given_name = data["GivenName"] or ""
populate_collection(person.middle_names, data.get("MiddleNames", None))
populate_collection(person.prefix_titles, data.get("PrefixTitles", None))
populate_collection(person.suffix_titles, data.get("SuffixTitles", None))
return {"FINISHED"}
class DisableEditingPerson(bpy.types.Operator): class DisableEditingPerson(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_person" bl_idname = "bim.disable_editing_person"
bl_label = "Disable Editing Person" bl_label = "Disable Editing Person"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def _execute(self, context):
context.scene.BIMOwnerProperties.active_person_id = 0 core.disable_editing_person(tool.PersonEditor())
return {"FINISHED"}
class AddPerson(bpy.types.Operator): class AddPerson(bpy.types.Operator, Operator):
bl_idname = "bim.add_person" bl_idname = "bim.add_person"
bl_label = "Add Person" bl_label = "Add Person"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
ifcopenshell.api.run("owner.add_person", IfcStore.get_file()) core.add_person(tool.Ifc())
Data.load(IfcStore.get_file())
return {"FINISHED"}
class EditPerson(bpy.types.Operator): class EditPerson(bpy.types.Operator, Operator):
bl_idname = "bim.edit_person" bl_idname = "bim.edit_person"
bl_label = "Edit Person" bl_label = "Edit Person"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.edit_person(tool.Ifc(), tool.PersonEditor())
props = context.scene.BIMOwnerProperties
person = props.person
attributes = {
"Identification": person.name or None,
"FamilyName": person.family_name or None,
"GivenName": person.given_name or None,
"MiddleNames": flatten_collection(person.middle_names),
"PrefixTitles": flatten_collection(person.prefix_titles),
"SuffixTitles": flatten_collection(person.suffix_titles),
}
if self.file.schema == "IFC2X3":
attributes["Id"] = attributes["Identification"]
del attributes["Identification"]
ifcopenshell.api.run(
"owner.edit_person",
self.file,
**{"person": self.file.by_id(props.active_person_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_person()
return {"FINISHED"}
class RemovePerson(bpy.types.Operator): class RemovePerson(bpy.types.Operator, Operator):
bl_idname = "bim.remove_person" bl_idname = "bim.remove_person"
bl_label = "Remove Person" bl_label = "Remove Person"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
person_id: bpy.props.IntProperty() person: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.remove_person(tool.Ifc(), person=tool.Ifc().get().by_id(self.person))
ifcopenshell.api.run("owner.remove_person", self.file, **{"person": self.file.by_id(self.person_id)})
Data.load(IfcStore.get_file())
return {"FINISHED"}
class EnableEditingRole(bpy.types.Operator): class AddPersonAttribute(bpy.types.Operator, Operator):
bl_idname = "bim.add_person_attribute"
bl_label = "Add Person Attribute"
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
def _execute(self, context):
core.add_person_attribute(tool.PersonEditor(), name=self.name)
class RemovePersonAttribute(bpy.types.Operator, Operator):
bl_idname = "bim.remove_person_attribute"
bl_label = "Remove Person Attribute"
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
id: bpy.props.IntProperty()
def _execute(self, context):
core.remove_person_attribute(tool.PersonEditor(), name=self.name, id=self.id)
class EnableEditingRole(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_role" bl_idname = "bim.enable_editing_role"
bl_label = "Enable Editing Role" bl_label = "Enable Editing Role"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
role_id: bpy.props.IntProperty() role: bpy.props.IntProperty()
def execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.enable_editing_role(tool.RoleEditor(), role=tool.Ifc().get().by_id(self.role))
props = context.scene.BIMOwnerProperties
props.active_role_id = self.role_id
data = Data.roles[self.role_id]
props.role.name = data["Role"]
props.role.user_defined_role = data["UserDefinedRole"] or ""
props.role.description = data["Description"] or ""
return {"FINISHED"}
class DisableEditingRole(bpy.types.Operator): class DisableEditingRole(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_role" bl_idname = "bim.disable_editing_role"
bl_label = "Disable Editing Role" bl_label = "Disable Editing Role"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def _execute(self, context):
context.scene.BIMOwnerProperties.active_role_id = 0 core.disable_editing_role(tool.RoleEditor())
return {"FINISHED"}
class AddRole(bpy.types.Operator): class AddRole(bpy.types.Operator, Operator):
bl_idname = "bim.add_role" bl_idname = "bim.add_role"
bl_label = "Add Role" bl_label = "Add Role"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
assigned_object_id: bpy.props.IntProperty() parent: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.add_role(tool.Ifc(), parent=tool.Ifc().get().by_id(self.parent))
ifcopenshell.api.run(
"owner.add_role", self.file, **{"assigned_object": self.file.by_id(self.assigned_object_id)}
)
Data.load(IfcStore.get_file())
return {"FINISHED"}
class EditRole(bpy.types.Operator): class EditRole(bpy.types.Operator, Operator):
bl_idname = "bim.edit_role" bl_idname = "bim.edit_role"
bl_label = "Edit Role" bl_label = "Edit Role"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.edit_role(tool.Ifc(), tool.RoleEditor())
props = context.scene.BIMOwnerProperties
attributes = {
"Role": props.role.name,
"UserDefinedRole": props.role.user_defined_role if props.role.name == "USERDEFINED" else None,
"Description": props.role.description or None,
}
ifcopenshell.api.run(
"owner.edit_role", self.file, **{"role": self.file.by_id(props.active_role_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_role()
return {"FINISHED"}
class RemoveRole(bpy.types.Operator): class RemoveRole(bpy.types.Operator, Operator):
bl_idname = "bim.remove_role" bl_idname = "bim.remove_role"
bl_label = "Remove Role" bl_label = "Remove Role"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
role_id: bpy.props.IntProperty() role: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.remove_role(tool.Ifc(), role=tool.Ifc().get().by_id(self.role))
ifcopenshell.api.run("owner.remove_role", self.file, **{"role": self.file.by_id(self.role_id)})
Data.load(IfcStore.get_file())
return {"FINISHED"}
class AddAddress(bpy.types.Operator): class AddAddress(bpy.types.Operator, Operator):
bl_idname = "bim.add_address" bl_idname = "bim.add_address"
bl_label = "Add Address" bl_label = "Add Address"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
assigned_object_id: bpy.props.IntProperty() parent: bpy.props.IntProperty()
ifc_class: bpy.props.StringProperty() ifc_class: bpy.props.StringProperty()
def execute(self, context): def _execute(self, context):
return IfcStore.execute_ifc_operator(self, context) core.add_address(tool.Ifc(), parent=tool.Ifc().get().by_id(self.parent), ifc_class=self.ifc_class)
class AddAddressAttribute(bpy.types.Operator, Operator):
bl_idname = "bim.add_address_attribute"
bl_label = "Add Address Attribute"
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.add_address_attribute(tool.AddressEditor, name=self.name)
ifcopenshell.api.run(
"owner.add_address",
self.file,
**{"assigned_object": self.file.by_id(self.assigned_object_id), "ifc_class": self.ifc_class}
)
Data.load(IfcStore.get_file())
return {"FINISHED"}
class EnableEditingAddress(bpy.types.Operator): class RemoveAddressAttribute(bpy.types.Operator, Operator):
bl_idname = "bim.remove_address_attribute"
bl_label = "Remove Address Attribute"
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
id: bpy.props.IntProperty()
def _execute(self, context):
core.remove_address_attribute(tool.AddressEditor, name=self.name, id=self.id)
class EnableEditingAddress(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_address" bl_idname = "bim.enable_editing_address"
bl_label = "Enable Editing Address" bl_label = "Enable Editing Address"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
address_id: bpy.props.IntProperty() address: bpy.props.IntProperty()
def execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.enable_editing_address(tool.AddressEditor(), address=tool.Ifc().get().by_id(self.address))
props = context.scene.BIMOwnerProperties
props.active_address_id = self.address_id
data = Data.addresses[self.address_id]
address = props.address
address.name = data["type"]
address.purpose = data["Purpose"] or "None"
address.description = data["Description"] or ""
address.user_defined_purpose = data["UserDefinedPurpose"] or ""
if data["type"] == "IfcTelecomAddress":
populate_collection(address.telephone_numbers, data.get("TelephoneNumbers", None))
populate_collection(address.facsimile_numbers, data.get("FacsimileNumbers", None))
address.pager_number = data["PagerNumber"] or ""
populate_collection(address.electronic_mail_addresses, data.get("ElectronicMailAddresses", None))
address.www_home_page_url = data["WWWHomePageURL"] or ""
if self.file.schema != "IFC2X3":
populate_collection(address.messaging_ids, data.get("MessagingIDs", None))
elif data["type"] == "IfcPostalAddress":
address.internal_location = data["InternalLocation"] or ""
populate_collection(address.address_lines, data.get("AddressLines", None))
address.postal_box = data["PostalBox"] or ""
address.town = data["Town"] or ""
address.region = data["Region"] or ""
address.postal_code = data["PostalCode"] or ""
address.country = data["Country"] or ""
return {"FINISHED"}
class DisableEditingAddress(bpy.types.Operator): class DisableEditingAddress(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_address" bl_idname = "bim.disable_editing_address"
bl_label = "Disable Editing Address" bl_label = "Disable Editing Address"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def _execute(self, context):
context.scene.BIMOwnerProperties.active_address_id = 0 core.disable_editing_address(tool.AddressEditor())
return {"FINISHED"}
class EditAddress(bpy.types.Operator): class EditAddress(bpy.types.Operator, Operator):
bl_idname = "bim.edit_address" bl_idname = "bim.edit_address"
bl_label = "Edit Address" bl_label = "Edit Address"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.edit_address(tool.Ifc, tool.AddressEditor)
props = context.scene.BIMOwnerProperties
attributes = {
"Purpose": props.address.purpose,
"UserDefinedPurpose": props.address.user_defined_purpose
if props.address.purpose == "USERDEFINED"
else None,
"Description": props.address.description or None,
}
address = self.file.by_id(props.active_address_id)
if address.is_a("IfcTelecomAddress"):
attributes.update(
{
"TelephoneNumbers": flatten_collection(props.address.telephone_numbers),
"FacsimileNumbers": flatten_collection(props.address.facsimile_numbers),
"PagerNumber": props.address.pager_number or None,
"ElectronicMailAddresses": flatten_collection(props.address.electronic_mail_addresses),
"WWWHomePageURL": props.address.www_home_page_url or None,
"MessagingIDs": flatten_collection(props.address.messaging_ids),
}
)
if self.file.schema == "IFC2X3":
del attributes["MessagingIDs"]
elif address.is_a("IfcPostalAddress"):
attributes.update(
{
"InternalLocation": props.address.internal_location or None,
"AddressLines": flatten_collection(props.address.address_lines),
"PostalBox": props.address.postal_box or None,
"Town": props.address.town or None,
"Region": props.address.region or None,
"PostalCode": props.address.postal_code or None,
"Country": props.address.country or None,
}
)
ifcopenshell.api.run("owner.edit_address", self.file, **{"address": address, "attributes": attributes})
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_address()
return {"FINISHED"}
class RemoveAddress(bpy.types.Operator): class RemoveAddress(bpy.types.Operator, Operator):
bl_idname = "bim.remove_address" bl_idname = "bim.remove_address"
bl_label = "Remove Address" bl_label = "Remove Address"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
address_id: bpy.props.IntProperty() address: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() core.remove_address(tool.Ifc(), address=tool.Ifc().get().by_id(self.address))
ifcopenshell.api.run("owner.remove_address", self.file, **{"address": self.file.by_id(self.address_id)})
Data.load(IfcStore.get_file())
return {"FINISHED"}
class EnableEditingOrganisation(bpy.types.Operator): class EnableEditingOrganisation(bpy.types.Operator):
@@ -67,71 +67,6 @@ def getOrganisations(self, context):
return _organisations_enum return _organisations_enum
class Address(PropertyGroup):
name: StringProperty(name="Name", default="IfcPostalAddress") # Stores IfcPostalAddress or IfcTelecomAddress
purpose: EnumProperty(
items=[
("None", "None", ""),
("OFFICE", "OFFICE", "An office address."),
("SITE", "SITE", "A site address."),
("HOME", "HOME", "A home address."),
("DISTRIBUTIONPOINT", "DISTRIBUTIONPOINT", "A postal distribution point address."),
("USERDEFINED", "USERDEFINED", "A user defined address type to be provided."),
],
name="Purpose",
)
description: StringProperty(name="Description")
user_defined_purpose: StringProperty(name="Custom Purpose")
internal_location: StringProperty(name="Internal Location")
address_lines: CollectionProperty(type=StrProperty, name="Address")
postal_box: StringProperty(name="Postal Box")
town: StringProperty(name="Town")
region: StringProperty(name="Region")
postal_code: StringProperty(name="Postal Code")
country: StringProperty(name="Country")
telephone_numbers: CollectionProperty(type=StrProperty, name="Telephone Numbers")
facsimile_numbers: CollectionProperty(type=StrProperty, name="Facsimile Numbers")
pager_number: StringProperty(name="Pager Number")
electronic_mail_addresses: CollectionProperty(type=StrProperty, name="Emails")
www_home_page_url: StringProperty(name="Website")
messaging_ids: CollectionProperty(type=StrProperty, name="IMs")
class Role(PropertyGroup):
name: EnumProperty(
items=[
("SUPPLIER", "SUPPLIER", ""),
("MANUFACTURER", "MANUFACTURER", ""),
("CONTRACTOR", "CONTRACTOR", ""),
("SUBCONTRACTOR", "SUBCONTRACTOR", ""),
("ARCHITECT", "ARCHITECT", ""),
("STRUCTURALENGINEER", "STRUCTURALENGINEER", ""),
("COSTENGINEER", "COSTENGINEER", ""),
("CLIENT", "CLIENT", ""),
("BUILDINGOWNER", "BUILDINGOWNER", ""),
("BUILDINGOPERATOR", "BUILDINGOPERATOR", ""),
("MECHANICALENGINEER", "MECHANICALENGINEER", ""),
("ELECTRICALENGINEER", "ELECTRICALENGINEER", ""),
("PROJECTMANAGER", "PROJECTMANAGER", ""),
("FACILITIESMANAGER", "FACILITIESMANAGER", ""),
("CIVILENGINEER", "CIVILENGINEER", ""),
("COMMISSIONINGENGINEER", "COMMISSIONINGENGINEER", ""),
("ENGINEER", "ENGINEER", ""),
("OWNER", "OWNER", ""),
("CONSULTANT", "CONSULTANT", ""),
("CONSTRUCTIONMANAGER", "CONSTRUCTIONMANAGER", ""),
("FIELDCONSTRUCTIONMANAGER", "FIELDCONSTRUCTIONMANAGER", ""),
("RESELLER", "RESELLER", ""),
("USERDEFINED", "USERDEFINED", ""),
],
name="Name",
)
user_defined_role: StringProperty(name="Custom Role")
description: StringProperty(name="Description")
class Organisation(PropertyGroup): class Organisation(PropertyGroup):
identification: StringProperty(name="Identification") identification: StringProperty(name="Identification")
name: StringProperty(name="Name") name: StringProperty(name="Name")
@@ -149,12 +84,21 @@ class Person(PropertyGroup):
class BIMOwnerProperties(PropertyGroup): class BIMOwnerProperties(PropertyGroup):
person: PointerProperty(type=Person) 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") active_person_id: IntProperty(name="Active Person Id")
organisation: PointerProperty(type=Organisation) organisation: PointerProperty(type=Organisation)
active_organisation_id: IntProperty(name="Active Organisation Id") active_organisation_id: IntProperty(name="Active Organisation Id")
role: PointerProperty(type=Role)
active_role_id: IntProperty(name="Active Role Id") active_role_id: IntProperty(name="Active Role Id")
address: PointerProperty(type=Address) role_attributes: CollectionProperty(name="Role Attributes", type=Attribute)
active_address_id: IntProperty(name="Active Address Id") active_address_id: IntProperty(name="Active Address Id")
address_attributes: CollectionProperty(name="Address Attributes", type=Attribute)
address_lines: CollectionProperty(type=StrProperty, name="Address")
telephone_numbers: CollectionProperty(type=StrProperty, name="Telephone Numbers")
facsimile_numbers: CollectionProperty(type=StrProperty, name="Facsimile Numbers")
electronic_mail_addresses: CollectionProperty(type=StrProperty, name="Emails")
messaging_ids: CollectionProperty(type=StrProperty, name="IMs")
user_person: EnumProperty(items=getPersons, name="Person") user_person: EnumProperty(items=getPersons, name="Person")
user_organisation: EnumProperty(items=getOrganisations, name="Organisation") user_organisation: EnumProperty(items=getOrganisations, name="Organisation")
@@ -17,10 +17,12 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import blenderbim.bim.helper
import blenderbim.tool as tool
from blenderbim.bim.module.owner.data import PeopleData
from bpy.types import Panel from bpy.types import Panel
from ifcopenshell.api.owner.data import Data from ifcopenshell.api.owner.data import Data
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from .operator import AddOrRemoveElementFromCollection
def draw_string_collection(layout, owner, collection_name): def draw_string_collection(layout, owner, collection_name):
@@ -31,12 +33,12 @@ def draw_string_collection(layout, owner, collection_name):
row = draw_prop_on_new_row( row = draw_prop_on_new_row(
column, collection[i], "name", align=True, text=f"{owner.bl_rna.properties[collection_name].name}" column, collection[i], "name", align=True, text=f"{owner.bl_rna.properties[collection_name].name}"
) )
add_op = row.operator(AddOrRemoveElementFromCollection.bl_idname, icon="ADD", text="") add_op = row.operator("bim.add_or_remove_element_from_collection", icon="ADD", text="")
add_op.operation = "+" add_op.operation = "+"
add_op.collection_path = collection.path_from_id() add_op.collection_path = collection.path_from_id()
else: else:
row = draw_prop_on_new_row(column, collection[i], "name", align=True, text=f"#{i + 1}") row = draw_prop_on_new_row(column, collection[i], "name", align=True, text=f"#{i + 1}")
rem_op = row.operator(AddOrRemoveElementFromCollection.bl_idname, icon="REMOVE", text="") rem_op = row.operator("bim.add_or_remove_element_from_collection", icon="REMOVE", text="")
rem_op.operation = "-" rem_op.operation = "-"
rem_op.collection_path = collection.path_from_id() rem_op.collection_path = collection.path_from_id()
rem_op.selected_item_idx = i rem_op.selected_item_idx = i
@@ -116,7 +118,7 @@ def draw_addresses_ui(box, assigned_object_id, addresses, file, context):
row.operator("bim.remove_address", icon="X", text="").address_id = address_id row.operator("bim.remove_address", icon="X", text="").address_id = address_id
class BIM_PT_people(Panel): class BIM_PT_people(bpy.types.Panel):
bl_label = "IFC People" bl_label = "IFC People"
bl_idname = "BIM_PT_people" bl_idname = "BIM_PT_people"
bl_options = {"DEFAULT_CLOSED"} bl_options = {"DEFAULT_CLOSED"}
@@ -126,47 +128,102 @@ class BIM_PT_people(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return IfcStore.get_file() return tool.Ifc().get()
def draw(self, context): def draw(self, context):
if not Data.is_loaded: if not PeopleData.is_loaded:
Data.load(IfcStore.get_file()) PeopleData.load()
self.file = IfcStore.get_file()
self.layout.use_property_split = True self.layout.use_property_split = True
self.layout.use_property_decorate = False self.layout.use_property_decorate = False
props = context.scene.BIMOwnerProperties
row = self.layout.row() row = self.layout.row()
row.operator("bim.add_person", icon="ADD") row.operator("bim.add_person", icon="ADD")
for person_id, person in Data.people.items(): for person in PeopleData.data["people"]:
if props.active_person_id == person_id: self.draw_person(person)
blender_person = props.person
box = self.layout.box() def draw_person(self, person):
row = draw_prop_on_new_row(box, blender_person, "name", align=True, icon="USER", text="") if person["is_editing"]:
row.operator("bim.edit_person", icon="CHECKMARK", text="") box = self.layout.box()
row.operator("bim.disable_editing_person", icon="CANCEL", text="") row = box.row(align=True)
draw_prop_on_new_row(box, blender_person, "family_name") row.operator("bim.edit_person", icon="CHECKMARK")
draw_prop_on_new_row(box, blender_person, "given_name") row.operator("bim.disable_editing_person", icon="CANCEL", text="")
draw_string_collection(box, blender_person, "middle_names") blenderbim.bim.helper.draw_attributes(person["props"], box)
draw_string_collection(box, blender_person, "prefix_titles")
draw_string_collection(box, blender_person, "suffix_titles") for attribute in person["list_attributes"]:
draw_roles_ui(box, person_id, person["Roles"], context) row = box.row(align=True)
draw_addresses_ui(box, person_id, person["Addresses"], self.file, context) row.label(text=attribute["name"])
op = row.operator("bim.add_person_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_person_attribute", icon="REMOVE", text="")
op.name = attribute["name"]
op.id = item["id"]
self.draw_roles(box, person)
self.draw_addresses(box, person)
else:
row = self.layout.row(align=True)
row.label(text=person["name"])
row.operator("bim.enable_editing_person", icon="GREASEPENCIL", text="").person = person["id"]
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: else:
row = self.layout.row(align=True) row = box.row(align=True)
name = person["Id"] if self.file.schema == "IFC2X3" else person["Identification"] row.label(text=role["label"])
name = name or "*" row.operator("bim.enable_editing_role", icon="GREASEPENCIL", text="").role = role["id"]
if person["GivenName"] or person["FamilyName"]: row.operator("bim.remove_role", icon="X", text="").role = role["id"]
full_name = "{} {}".format(person["GivenName"] or "", person["FamilyName"] or "").strip()
name += f" ({full_name})" def draw_addresses(self, box, person):
row.label(text=name) row = box.row(align=True)
if person["Roles"]: row.label(text="Addresses")
row.label(text=", ".join([Data.roles[r]["Role"] for r in person["Roles"]])) op = row.operator("bim.add_address", icon="LINK_BLEND", text="")
row.operator("bim.enable_editing_person", icon="GREASEPENCIL", text="").person_id = person_id op.parent = person["id"]
if not person["is_engaged"]: op.ifc_class = "IfcTelecomAddress"
row.operator("bim.remove_person", icon="X", text="").person_id = person_id 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): class BIM_PT_organisations(Panel):
@@ -21,11 +21,7 @@ import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import blenderbim.bim.schema import blenderbim.bim.schema
import blenderbim.bim.handler import blenderbim.bim.handler
from blenderbim.bim.module.pset_template.prop import ( from blenderbim.bim.module.pset_template.prop import updatePsetTemplateFiles, updatePsetTemplates, getPsetTemplates
updatePsetTemplateFiles,
updatePsetTemplates,
getPsetTemplates
)
from ifcopenshell.api.pset_template.data import Data from ifcopenshell.api.pset_template.data import Data
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
+100
View File
@@ -0,0 +1,100 @@
# 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/>.
def add_person(ifc):
return ifc.run("owner.add_person")
def remove_person(ifc, person=None):
ifc.run("owner.remove_person", person=person)
def enable_editing_person(person_editor, person=None):
person_editor.set_person(person)
person_editor.import_attributes()
def disable_editing_person(person_editor):
person_editor.clear_person()
def edit_person(ifc, person_editor):
ifc.run("owner.edit_person", person=person_editor.get_person(), attributes=person_editor.export_attributes())
disable_editing_person(person_editor)
def add_person_attribute(person_editor, name=None):
person_editor.add_attribute(name)
def remove_person_attribute(person_editor, name=None, id=None):
person_editor.remove_attribute(name, id)
def add_role(ifc, parent=None):
return ifc.run("owner.add_role", assigned_object=parent)
def remove_role(ifc, role=None):
ifc.run("owner.remove_role", role=role)
def enable_editing_role(role_editor, role=None):
role_editor.set_role(role)
role_editor.import_attributes()
def disable_editing_role(role_editor):
role_editor.clear_role()
def edit_role(ifc, role_editor):
ifc.run("owner.edit_role", role=role_editor.get_role(), attributes=role_editor.export_attributes())
role_editor.clear_role()
def add_address(ifc, parent=None, ifc_class="IfcPostalAddress"):
return ifc.run("owner.add_address", assigned_object=parent, ifc_class=ifc_class)
def remove_address(ifc, address=None):
ifc.run("owner.remove_address", address=address)
def enable_editing_address(address_editor, address=None):
address_editor.set_address(address)
address_editor.import_attributes()
def disable_editing_address(address_editor):
address_editor.clear_address()
def edit_address(ifc, address_editor):
address = address_editor.get_address()
ifc.run("owner.edit_address", address=address, attributes=address_editor.export_attributes())
address_editor.clear_address()
def add_address_attribute(address_editor, name=None):
address_editor.add_attribute(name)
def remove_address_attribute(address_editor, name=None, id=None):
address_editor.remove_attribute(name, id)
@@ -0,0 +1,23 @@
# 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/>.
from blenderbim.core.tool.ifc import Ifc
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
@@ -0,0 +1,56 @@
# 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 AddressEditor(abc.ABC):
@classmethod
@abc.abstractmethod
def set_address(cls, address):
pass
@classmethod
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_address(cls):
pass
@classmethod
@abc.abstractmethod
def get_address(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def add_attribute(cls, name):
pass
@classmethod
@abc.abstractmethod
def remove_attribute(cls, name, id):
pass
@@ -0,0 +1,23 @@
# 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 Blender(abc.ABC):
pass
@@ -16,15 +16,11 @@
# You should have received a copy of the GNU General Public License # 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/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import abc
class Ifc:
class Ifc(abc.ABC):
@classmethod @classmethod
@abc.abstractmethod
def run(cls, command, **kwargs): def run(cls, command, **kwargs):
"""Runs an operation on the active IFC dataset"""
pass
class Blender:
@classmethod
def get_ifc(cls):
pass pass
@@ -0,0 +1,56 @@
# 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 PersonEditor(abc.ABC):
@classmethod
@abc.abstractmethod
def set_person(cls, person):
pass
@classmethod
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_person(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def get_person(cls):
pass
@classmethod
@abc.abstractmethod
def add_attribute(cls, name):
pass
@classmethod
@abc.abstractmethod
def remove_attribute(cls, name, id):
pass
@@ -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 RoleEditor(abc.ABC):
@classmethod
@abc.abstractmethod
def set_role(cls, role):
pass
@classmethod
@abc.abstractmethod
def import_attributes(cls):
pass
@classmethod
@abc.abstractmethod
def clear_role(cls):
pass
@classmethod
@abc.abstractmethod
def get_role(cls):
pass
@classmethod
@abc.abstractmethod
def export_attributes(cls):
pass
@@ -0,0 +1,23 @@
# 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/>.
from blenderbim.tool.ifc import Ifc
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
@@ -0,0 +1,108 @@
# 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 blenderbim.core.tool
import blenderbim.bim.helper
import blenderbim.tool as tool
class AddressEditor(blenderbim.core.tool.AddressEditor):
@classmethod
def set_address(cls, address):
bpy.context.scene.BIMOwnerProperties.active_address_id = address.id()
@classmethod
def import_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
props.address_attributes.clear()
props.address_lines.clear()
props.telephone_numbers.clear()
props.facsimile_numbers.clear()
props.electronic_mail_addresses.clear()
props.messaging_ids.clear()
address = cls.get_address()
def callback(name, prop, data):
if name == "AddressLines":
for line in data[name] or []:
props.address_lines.add().name = line
elif name == "TelephoneNumbers":
for line in data[name] or []:
props.telephone_numbers.add().name = line
elif name == "FacsimileNumbers":
for line in data[name] or []:
props.facsimile_numbers.add().name = line
elif name == "ElectronicMailAddresses":
for line in data[name] or []:
props.electronic_mail_addresses.add().name = line
elif name == "MessagingIDs":
for line in data[name] or []:
props.messaging_ids.add().name = line
blenderbim.bim.helper.import_attributes(address.is_a(), props.address_attributes, address.get_info(), callback)
@classmethod
def clear_address(cls):
bpy.context.scene.BIMOwnerProperties.active_address_id = 0
@classmethod
def get_address(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_address_id)
@classmethod
def export_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.address_attributes)
if cls.get_address().is_a("IfcPostalAddress"):
attributes["AddressLines"] = [l.name for l in props.address_lines] or None
elif cls.get_address().is_a("IfcTelecomAddress"):
attributes["TelephoneNumbers"] = [l.name for l in props.telephone_numbers] or None
attributes["FacsimileNumbers"] = [l.name for l in props.facsimile_numbers] or None
attributes["ElectronicMailAddresses"] = [l.name for l in props.electronic_mail_addresses] or None
attributes["MessagingIDs"] = [l.name for l in props.messaging_ids] or None
return attributes
@classmethod
def add_attribute(cls, name):
props = bpy.context.scene.BIMOwnerProperties
if name == "AddressLines":
props.address_lines.add()
elif name == "TelephoneNumbers":
props.telephone_numbers.add()
elif name == "FacsimileNumbers":
props.facsimile_numbers.add()
elif name == "ElectronicMailAddresses":
props.electronic_mail_addresses.add()
elif name == "MessagingIDs":
props.messaging_ids.add()
@classmethod
def remove_attribute(cls, name, id):
props = bpy.context.scene.BIMOwnerProperties
if name == "AddressLines":
props.address_lines.remove(id)
elif name == "TelephoneNumbers":
props.telephone_numbers.remove(id)
elif name == "FacsimileNumbers":
props.facsimile_numbers.remove(id)
elif name == "ElectronicMailAddresses":
props.electronic_mail_addresses.remove(id)
elif name == "MessagingIDs":
props.messaging_ids.remove(id)
@@ -16,17 +16,11 @@
# You should have received a copy of the GNU General Public License # 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/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell.api import ifcopenshell.api
import blenderbim.core.tool
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
class Ifc:
@classmethod
def run(cls, command, **kwargs):
return ifcopenshell.api.run(command, IfcStore.get_file(), **kwargs)
class Blender: class Blender:
@classmethod pass
def get_ifc(cls):
return "ifc"
+40
View File
@@ -0,0 +1,40 @@
# 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
from blenderbim.bim.ifc import IfcStore
class Ifc:
@classmethod
def run(cls, command, **kwargs):
return ifcopenshell.api.run(command, IfcStore.get_file(), **kwargs)
@classmethod
def set(cls, ifc):
IfcStore.file = ifc
@classmethod
def get(cls):
return IfcStore.get_file()
@classmethod
def get_schema(cls):
return IfcStore.get_file().schema
@@ -0,0 +1,86 @@
# 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 PersonEditor(blenderbim.core.tool.person_editor.PersonEditor):
@classmethod
def set_person(cls, person):
bpy.context.scene.BIMOwnerProperties.active_person_id = person.id()
@classmethod
def import_attributes(cls):
person = tool.Ifc.get().by_id(bpy.context.scene.BIMOwnerProperties.active_person_id)
props = bpy.context.scene.BIMOwnerProperties
props.person_attributes.clear()
props.middle_names.clear()
props.prefix_titles.clear()
props.suffix_titles.clear()
def callback(name, prop, data):
if name == "MiddleNames":
for name in data["MiddleNames"] or []:
props.middle_names.add().name = name or ""
if name == "PrefixTitles":
for name in data["PrefixTitles"] or []:
props.prefix_titles.add().name = name or ""
if name == "SuffixTitles":
for name in data["SuffixTitles"] or []:
props.suffix_titles.add().name = name or ""
blenderbim.bim.helper.import_attributes("IfcPerson", props.person_attributes, person.get_info(), callback)
@classmethod
def clear_person(cls):
bpy.context.scene.BIMOwnerProperties.active_person_id = 0
@classmethod
def export_attributes(cls):
props = bpy.context.scene.BIMOwnerProperties
attributes = blenderbim.bim.helper.export_attributes(props.person_attributes)
attributes["MiddleNames"] = [v.name for v in props.middle_names] if props.middle_names else None
attributes["PrefixTitles"] = [v.name for v in props.prefix_titles] if props.prefix_titles else None
attributes["SuffixTitles"] = [v.name for v in props.suffix_titles] if props.suffix_titles else None
return attributes
@classmethod
def get_person(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_person_id)
@classmethod
def add_attribute(cls, name):
if name == "MiddleNames":
bpy.context.scene.BIMOwnerProperties.middle_names.add()
elif name == "PrefixTitles":
bpy.context.scene.BIMOwnerProperties.prefix_titles.add()
elif name == "SuffixTitles":
bpy.context.scene.BIMOwnerProperties.suffix_titles.add()
@classmethod
def remove_attribute(cls, name, id):
if name == "MiddleNames":
bpy.context.scene.BIMOwnerProperties.middle_names.remove(id)
elif name == "PrefixTitles":
bpy.context.scene.BIMOwnerProperties.prefix_titles.remove(id)
elif name == "SuffixTitles":
bpy.context.scene.BIMOwnerProperties.suffix_titles.remove(id)
@@ -0,0 +1,47 @@
# 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 blenderbim.core.tool
import blenderbim.tool as tool
import blenderbim.bim.helper
class RoleEditor(blenderbim.core.tool.role_editor.RoleEditor):
@classmethod
def set_role(cls, role):
bpy.context.scene.BIMOwnerProperties.active_role_id = role.id()
@classmethod
def import_attributes(cls):
role = cls.get_role()
props = bpy.context.scene.BIMOwnerProperties
props.role_attributes.clear()
blenderbim.bim.helper.import_attributes("IfcActorRole", props.role_attributes, role.get_info())
@classmethod
def clear_role(cls):
bpy.context.scene.BIMOwnerProperties.active_role_id = 0
@classmethod
def get_role(cls):
return tool.Ifc().get().by_id(bpy.context.scene.BIMOwnerProperties.active_role_id)
@classmethod
def export_attributes(cls):
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMOwnerProperties.role_attributes)
+12 -1
View File
@@ -32,7 +32,7 @@ from mathutils import Vector
webbrowser.open = lambda x: True webbrowser.open = lambda x: True
variables = {"cwd": os.getcwd()} variables = {"cwd": os.getcwd(), "ifc": "IfcStore.get_file()"}
class NewFile: class NewFile:
@@ -45,6 +45,17 @@ class NewFile:
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True) bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
class NewIfc:
@pytest.fixture(autouse=True)
def setup(self):
IfcStore.purge()
bpy.ops.wm.read_homefile(app_template="")
while bpy.data.objects:
bpy.data.objects.remove(bpy.data.objects[0])
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
bpy.ops.bim.create_project()
def scenario(function): def scenario(function):
def subfunction(self): def subfunction(self):
run(function(self)) run(function(self))
@@ -0,0 +1,17 @@
# 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/>.
@@ -0,0 +1,279 @@
# 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 test.bim.bootstrap
class TestAddPerson(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
When I press "bim.add_person"
Then nothing interesting happens
"""
class TestRemovePerson(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
When I press "bim.remove_person(person={person})"
Then nothing interesting happens
"""
class TestAddPersonAttribute(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
When I press "bim.add_person_attribute(name='MiddleNames')"
And I press "bim.add_person_attribute(name='PrefixTitles')"
And I press "bim.add_person_attribute(name='SuffixTitles')"
Then nothing interesting happens
"""
class TestRemovePersonAttribute(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
When I press "bim.add_person_attribute(name='MiddleNames')"
And I press "bim.remove_person_attribute(name='MiddleNames', id=0)"
And I press "bim.add_person_attribute(name='PrefixTitles')"
And I press "bim.remove_person_attribute(name='PrefixTitles', id=0)"
And I press "bim.add_person_attribute(name='SuffixTitles')"
And I press "bim.remove_person_attribute(name='SuffixTitles', id=0)"
Then nothing interesting happens
"""
class TestEnableEditingPerson(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
When I press "bim.enable_editing_person(person={person})"
Then "scene.BIMOwnerProperties.active_person_id" is "{person}"
"""
class TestDisableEditingPerson(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.enable_editing_person(person={person})"
When I press "bim.disable_editing_person"
Then "scene.BIMOwnerProperties.active_person_id" is "0"
"""
class TestEditPerson(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.enable_editing_person(person={person})"
When I press "bim.edit_person"
Then "scene.BIMOwnerProperties.active_person_id" is "0"
"""
class TestAddRole(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
When I press "bim.add_role(parent={person})"
Then nothing interesting happens
"""
class TestRemoveRole(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_role(parent={person})"
And the variable "role" is "{ifc}.by_type('IfcActorRole')[0].id()"
When I press "bim.remove_role(role={role})"
Then nothing interesting happens
"""
class TestEnableEditingRole(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_role(parent={person})"
And the variable "role" is "{ifc}.by_type('IfcActorRole')[0].id()"
When I press "bim.enable_editing_role(role={role})"
Then nothing interesting happens
"""
class TestDisableEditingRole(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_role(parent={person})"
And the variable "role" is "{ifc}.by_type('IfcActorRole')[0].id()"
And I press "bim.enable_editing_role(role={role})"
When I press "bim.disable_editing_role()"
Then nothing interesting happens
"""
class TestEditRole(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_role(parent={person})"
And the variable "role" is "{ifc}.by_type('IfcActorRole')[0].id()"
And I press "bim.enable_editing_role(role={role})"
When I press "bim.edit_role()"
Then nothing interesting happens
"""
class TestAddAddress(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
When I press "bim.add_address(parent={person}, ifc_class='IfcPostalAddress')"
Then nothing interesting happens
"""
class TestAddAddressAttribute(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_address(parent={person}, ifc_class='IfcPostalAddress')"
And the variable "address" is "{ifc}.by_type('IfcPostalAddress')[0].id()"
And I press "bim.enable_editing_address(address={address})"
When I press "bim.add_address_attribute(name='AddressLines')"
Then nothing interesting happens
"""
class TestRemoveAddressAttribute(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_address(parent={person}, ifc_class='IfcPostalAddress')"
And the variable "address" is "{ifc}.by_type('IfcPostalAddress')[0].id()"
And I press "bim.enable_editing_address(address={address})"
And I press "bim.add_address_attribute(name='AddressLines')"
When I press "bim.remove_address_attribute(name='AddressLines', id=0)"
Then nothing interesting happens
"""
class TestRemoveAddress(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_address(parent={person}, ifc_class='IfcPostalAddress')"
And the variable "address" is "{ifc}.by_type('IfcPostalAddress')[0].id()"
When I press "bim.remove_address(address={address})"
Then nothing interesting happens
"""
class TestEnableEditingAddress(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_address(parent={person}, ifc_class='IfcPostalAddress')"
And the variable "address" is "{ifc}.by_type('IfcPostalAddress')[0].id()"
When I press "bim.enable_editing_address(address={address})"
Then nothing interesting happens
"""
class TestDisableEditingAddress(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_address(parent={person}, ifc_class='IfcPostalAddress')"
And the variable "address" is "{ifc}.by_type('IfcPostalAddress')[0].id()"
And I press "bim.enable_editing_address(address={address})"
When I press "bim.disable_editing_address()"
Then nothing interesting happens
"""
class TestEditAddress(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_run(self):
return """
Given an empty IFC project
And I press "bim.add_person"
And the variable "person" is "{ifc}.by_type('IfcPerson')[0].id()"
And I press "bim.add_address(parent={person}, ifc_class='IfcPostalAddress')"
And the variable "address" is "{ifc}.by_type('IfcPostalAddress')[0].id()"
And I press "bim.enable_editing_address(address={address})"
When I press "bim.edit_address()"
Then nothing interesting happens
"""
+26 -22
View File
@@ -35,25 +35,25 @@ def blender():
prophet.verify() prophet.verify()
def subject(sus): @pytest.fixture
def decorate(cls): def person_editor():
cls.sus = sus prophet = Prophecy(blenderbim.core.tool.PersonEditor)
return cls yield prophet
prophet.verify()
return decorate
class Spec: @pytest.fixture
@pytest.fixture(autouse=True) def role_editor():
def setup(self): prophet = Prophecy(blenderbim.core.tool.RoleEditor)
self.subject = None yield prophet
prophet.verify()
def construct_with(self, *args, **kwargs):
self.subject = self.sus(*args, **kwargs)
return self.subject
def predict(self, cls): @pytest.fixture
return Prophecy(cls) def address_editor():
prophet = Prophecy(blenderbim.core.tool.AddressEditor)
yield prophet
prophet.verify()
class Prophecy: class Prophecy:
@@ -66,10 +66,12 @@ class Prophecy:
def __getattr__(self, attr): def __getattr__(self, attr):
if not hasattr(self.subject, attr): if not hasattr(self.subject, attr):
raise AttributeError(f"Prophecy has no attribute {attr}") raise AttributeError(f"Prophecy {self.subject} has no attribute {attr}")
def decorate(*args, **kwargs): def decorate(*args, **kwargs):
call = {"name": attr, "args": args, "kwargs": kwargs} call = {"name": attr, "args": args, "kwargs": kwargs}
# Ensure that signature is valid
getattr(self.subject, attr)(*args, **kwargs)
try: try:
key = json.dumps(call, sort_keys=True) key = json.dumps(call, sort_keys=True)
self.calls.append(call) self.calls.append(call)
@@ -81,23 +83,25 @@ class Prophecy:
return decorate return decorate
def should(self): def should_be_called(self, number=None):
self.should_call = self.calls.pop() self.should_call = self.calls.pop()
return self
def be_called(self, number=None):
self.predictions.append({"type": "SHOULD_BE_CALLED", "number": number, "call": self.should_call}) self.predictions.append({"type": "SHOULD_BE_CALLED", "number": number, "call": self.should_call})
return self return self
def return_with(self, value): def will_return(self, value):
key = json.dumps(self.should_call, sort_keys=True) key = json.dumps(self.should_call, sort_keys=True)
self.return_values[key] = value self.return_values[key] = value
return self return self
def verify(self): def verify(self):
predicted_calls = []
for prediction in self.predictions: for prediction in self.predictions:
predicted_calls.append(prediction["call"])
if prediction["type"] == "SHOULD_BE_CALLED": if prediction["type"] == "SHOULD_BE_CALLED":
self.verify_should_be_called(prediction) self.verify_should_be_called(prediction)
for call in self.calls:
if call not in predicted_calls:
raise Exception(f"Unpredicted call: {call}")
def verify_should_be_called(self, prediction): def verify_should_be_called(self, prediction):
if prediction["number"]: if prediction["number"]:
@@ -106,4 +110,4 @@ class Prophecy:
raise Exception(f"Called {count}: {prediction}") raise Exception(f"Called {count}: {prediction}")
else: else:
if prediction["call"] not in self.calls: if prediction["call"] not in self.calls:
raise Exception("Not called", prediction) raise Exception(f"{self.subject} was not called with {prediction['call']['name']}: {prediction}")
+149
View File
@@ -0,0 +1,149 @@
# 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 blenderbim.core.owner as subject
from test.core.bootstrap import ifc, blender, person_editor, role_editor, address_editor
class TestAddPerson:
def test_run(self, ifc):
ifc.run("owner.add_person").should_be_called().will_return("person")
assert subject.add_person(ifc) == "person"
class TestRemovePerson:
def test_run(self, ifc):
ifc.run("owner.remove_person", person="person").should_be_called()
subject.remove_person(ifc, person="person")
class TestEnableEditingPerson:
def test_run(self, person_editor):
person_editor.set_person("person").should_be_called()
person_editor.import_attributes().should_be_called()
subject.enable_editing_person(person_editor, person="person")
class TestDisableEditingPerson:
def test_run(self, person_editor):
person_editor.clear_person().should_be_called()
subject.disable_editing_person(person_editor)
class TestEditPerson:
def test_run(self, ifc, person_editor):
person_editor.get_person().should_be_called().will_return("person")
person_editor.export_attributes().should_be_called().will_return("attributes")
ifc.run("owner.edit_person", person="person", attributes="attributes").should_be_called()
person_editor.clear_person().should_be_called()
subject.edit_person(ifc, person_editor)
class TestAddPersonAttribute:
def test_run(self, person_editor):
person_editor.add_attribute("name").should_be_called()
subject.add_person_attribute(person_editor, name="name")
class TestRemovePersonAttribute:
def test_run(self, person_editor):
person_editor.remove_attribute("name", "id").should_be_called()
subject.remove_person_attribute(person_editor, name="name", id="id")
class TestAddRole:
def test_run(self, ifc):
ifc.run("owner.add_role", assigned_object="parent").should_be_called().will_return("role")
assert subject.add_role(ifc, parent="parent") == "role"
class TestRemoveRole:
def test_run(self, ifc):
ifc.run("owner.remove_role", role="role").should_be_called()
subject.remove_role(ifc, role="role")
class TestEnableEditingRole:
def test_run(self, role_editor):
role_editor.set_role("role").should_be_called()
role_editor.import_attributes().should_be_called()
subject.enable_editing_role(role_editor, role="role")
class TestDisableEditingRole:
def test_run(self, role_editor):
role_editor.clear_role().should_be_called()
subject.disable_editing_role(role_editor)
class TestEditRole:
def test_run(self, ifc, role_editor):
role_editor.export_attributes().should_be_called().will_return("attributes")
role_editor.get_role().should_be_called().will_return("role")
ifc.run("owner.edit_role", role="role", attributes="attributes").should_be_called()
role_editor.clear_role().should_be_called()
subject.edit_role(ifc, role_editor)
class TestAddAddress:
def test_run(self, ifc):
ifc.run(
"owner.add_address", assigned_object="parent", ifc_class="IfcPostalAddress"
).should_be_called().will_return("address")
assert subject.add_address(ifc, parent="parent", ifc_class="IfcPostalAddress") == "address"
class TestRemoveAddress:
def test_run(self, ifc):
ifc.run("owner.remove_address", address="address").should_be_called()
subject.remove_address(ifc, address="address")
class TestEnableEditingAddress:
def test_run(self, address_editor):
address_editor.set_address("address").should_be_called()
address_editor.import_attributes().should_be_called()
subject.enable_editing_address(address_editor, address="address")
class TestDisableEditingAddress:
def test_run(self, address_editor):
address_editor.clear_address().should_be_called()
subject.disable_editing_address(address_editor)
class TestEditAddress:
def test_run(self, ifc, address_editor):
address_editor.get_address().should_be_called().will_return("address")
address_editor.export_attributes().should_be_called().will_return("attributes")
ifc.run("owner.edit_address", address="address", attributes="attributes").should_be_called()
address_editor.clear_address().should_be_called()
subject.edit_address(ifc, address_editor)
class TestAddAddressAttribute:
def test_run(self, address_editor):
address_editor.add_attribute("name").should_be_called()
subject.add_address_attribute(address_editor, name="name")
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")
+17
View File
@@ -0,0 +1,17 @@
# 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/>.
@@ -0,0 +1,197 @@
# 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.address_editor import AddressEditor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.AddressEditor)
class TestSetAddress(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
address = ifc.createIfcPostalAddress()
subject().set_address(address)
assert bpy.context.scene.BIMOwnerProperties.active_address_id == address.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_importing_a_postal_address(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
address = ifc.createIfcPostalAddress()
address.Purpose = "USERDEFINED"
address.Description = "Description"
address.UserDefinedPurpose = "UserDefinedPurpose"
address.InternalLocation = "InternalLocation"
address.AddressLines = ["Address", "Lines"]
address.PostalBox = "PostalBox"
address.Town = "Town"
address.Region = "Region"
address.PostalCode = "PostalCode"
address.Country = "Country"
subject().set_address(address)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "USERDEFINED"
assert props.address_attributes.get("Description").string_value == "Description"
assert props.address_attributes.get("UserDefinedPurpose").string_value == "UserDefinedPurpose"
assert props.address_attributes.get("InternalLocation").string_value == "InternalLocation"
assert props.address_attributes.get("PostalBox").string_value == "PostalBox"
assert props.address_attributes.get("Town").string_value == "Town"
assert props.address_attributes.get("Region").string_value == "Region"
assert props.address_attributes.get("PostalCode").string_value == "PostalCode"
assert props.address_attributes.get("Country").string_value == "Country"
assert len(props.address_lines) == 2
assert props.address_lines[0].name == "Address"
assert props.address_lines[1].name == "Lines"
def test_importing_a_postal_address_twice(self):
self.test_importing_a_postal_address()
ifc = tool.Ifc().get()
address = ifc.createIfcPostalAddress()
address.Purpose = "OFFICE"
subject().set_address(address)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "OFFICE"
assert len(props.address_lines) == 0
def test_importing_a_telecom_address(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
address = ifc.createIfcTelecomAddress()
address.Purpose = "USERDEFINED"
address.Description = "Description"
address.UserDefinedPurpose = "UserDefinedPurpose"
address.TelephoneNumbers = ["Telephone", "Numbers"]
address.FacsimileNumbers = ["Facsimile", "Numbers"]
address.PagerNumber = "PagerNumber"
address.ElectronicMailAddresses = ["Electronic", "Mail", "Addresses"]
address.WWWHomePageURL = "WWWHomePageURL"
address.MessagingIDs = ["Messaging", "IDs"]
subject().set_address(address)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "USERDEFINED"
assert props.address_attributes.get("Description").string_value == "Description"
assert props.address_attributes.get("UserDefinedPurpose").string_value == "UserDefinedPurpose"
assert [a.name for a in props.telephone_numbers] == ["Telephone", "Numbers"]
assert [a.name for a in props.facsimile_numbers] == ["Facsimile", "Numbers"]
assert [a.name for a in props.electronic_mail_addresses] == ["Electronic", "Mail", "Addresses"]
assert [a.name for a in props.messaging_ids] == ["Messaging", "IDs"]
def test_importing_a_telecom_address_twice(self):
self.test_importing_a_telecom_address()
ifc = tool.Ifc().get()
address = ifc.createIfcTelecomAddress()
address.Purpose = "OFFICE"
subject().set_address(address)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.address_attributes.get("Purpose").enum_value == "OFFICE"
assert len(props.telephone_numbers) == 0
assert len(props.facsimile_numbers) == 0
assert len(props.electronic_mail_addresses) == 0
assert len(props.messaging_ids) == 0
class TestClearAddress(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
address = ifc.createIfcPostalAddress()
subject().set_address(address)
subject().clear_address()
assert bpy.context.scene.BIMOwnerProperties.active_address_id == 0
class TestGetAddress(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
address = ifc.createIfcPostalAddress()
subject().set_address(address)
assert subject().get_address() == address
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_exporting_a_postal_address(self):
TestImportAttributes().test_importing_a_postal_address()
assert subject().export_attributes() == {
"Purpose": "USERDEFINED",
"Description": "Description",
"UserDefinedPurpose": "UserDefinedPurpose",
"InternalLocation": "InternalLocation",
"AddressLines": ["Address", "Lines"],
"PostalBox": "PostalBox",
"Town": "Town",
"Region": "Region",
"PostalCode": "PostalCode",
"Country": "Country",
}
def test_exporting_a_telecom_address(self):
TestImportAttributes().test_importing_a_telecom_address()
assert subject().export_attributes() == {
"Purpose": "USERDEFINED",
"Description": "Description",
"UserDefinedPurpose": "UserDefinedPurpose",
"TelephoneNumbers": ["Telephone", "Numbers"],
"FacsimileNumbers": ["Facsimile", "Numbers"],
"PagerNumber": "PagerNumber",
"ElectronicMailAddresses": ["Electronic", "Mail", "Addresses"],
"WWWHomePageURL": "WWWHomePageURL",
"MessagingIDs": ["Messaging", "IDs"],
}
class TestAddAttribute(test.bim.bootstrap.NewFile):
def test_run(self):
subject().add_attribute("AddressLines")
subject().add_attribute("TelephoneNumbers")
subject().add_attribute("FacsimileNumbers")
subject().add_attribute("ElectronicMailAddresses")
subject().add_attribute("MessagingIDs")
props = bpy.context.scene.BIMOwnerProperties
assert len(props.address_lines) == 1
assert len(props.telephone_numbers) == 1
assert len(props.facsimile_numbers) == 1
assert len(props.electronic_mail_addresses) == 1
assert len(props.messaging_ids) == 1
class TestRemoveAddress(test.bim.bootstrap.NewFile):
TestAddAttribute().test_run()
subject().remove_attribute("AddressLines", 0)
subject().remove_attribute("TelephoneNumbers", 0)
subject().remove_attribute("FacsimileNumbers", 0)
subject().remove_attribute("ElectronicMailAddresses", 0)
subject().remove_attribute("MessagingIDs", 0)
props = bpy.context.scene.BIMOwnerProperties
assert len(props.address_lines) == 0
assert len(props.telephone_numbers) == 0
assert len(props.facsimile_numbers) == 0
assert len(props.electronic_mail_addresses) == 0
assert len(props.messaging_ids) == 0
+17
View File
@@ -0,0 +1,17 @@
# 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/>.
+58
View File
@@ -0,0 +1,58 @@
# 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
from blenderbim.tool import Ifc as subject
class TestSet(test.bim.bootstrap.NewFile):
def test_setting_an_ifc_data(self):
ifc = ifcopenshell.file()
subject().set(ifc)
assert subject().get() == ifc
class TestGet(test.bim.bootstrap.NewFile):
def test_getting_an_ifc_dataset_from_a_ifc_spf_filepath(self):
assert subject().get() is None
bpy.context.scene.BIMProperties.ifc_file = "test/files/basic.ifc"
result = subject().get()
assert isinstance(result, ifcopenshell.file)
def test_getting_the_active_ifc_dataset_regardless_of_ifc_path(self):
bpy.context.scene.BIMProperties.ifc_file = "test/files/basic.ifc"
ifc = ifcopenshell.file()
subject().set(ifc)
assert subject().get() == ifc
class TestRun(test.bim.bootstrap.NewFile):
def test_running_a_command_on_the_active_ifc_dataset(self):
ifc = ifcopenshell.file()
subject().set(ifc)
wall = subject().run("root.create_entity", ifc_class="IfcWall")
assert subject().get().by_type("IfcWall")[0] == wall
class TestGetSchema(test.bim.bootstrap.NewFile):
def test_getting_the_schema_version_identifier(self):
ifc = ifcopenshell.file(schema="IFC4")
subject().set(ifc)
assert subject().get_schema() == "IFC4"
@@ -0,0 +1,152 @@
# 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.person_editor import PersonEditor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.person_editor.PersonEditor)
class TestSetPerson(test.bim.bootstrap.NewFile):
def test_run(self):
person = ifcopenshell.file().createIfcPerson()
subject().set_person(person)
assert bpy.context.scene.BIMOwnerProperties.active_person_id == person.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
person.Identification = "identification"
person.GivenName = "given_name"
person.FamilyName = "family_name"
person.MiddleNames = ("middle", "names")
person.PrefixTitles = ("prefix", "titles")
person.SuffixTitles = ("suffix", "titles")
subject().set_person(person)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.person_attributes.get("Identification").string_value == "identification"
assert props.person_attributes.get("GivenName").string_value == "given_name"
assert props.person_attributes.get("FamilyName").string_value == "family_name"
assert len(props.middle_names) == 2
assert props.middle_names[0].name == "middle"
assert props.middle_names[1].name == "names"
assert len(props.prefix_titles) == 2
assert props.prefix_titles[0].name == "prefix"
assert props.prefix_titles[1].name == "titles"
assert len(props.suffix_titles) == 2
assert props.suffix_titles[0].name == "suffix"
assert props.suffix_titles[1].name == "titles"
def test_overwriting_a_previous_import(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
person.Identification = "identification"
person.GivenName = "given_name"
subject().set_person(person)
subject().import_attributes()
person.Identification = "identification2"
person.GivenName = None
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.person_attributes.get("Identification").string_value == "identification2"
assert props.person_attributes.get("GivenName").string_value == ""
class TestClearPerson(test.bim.bootstrap.NewFile):
def test_run(self):
props = bpy.context.scene.BIMOwnerProperties
props.active_person_id = 1
subject().clear_person()
assert props.active_person_id == 0
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
person.Identification = "identification"
person.GivenName = "given_name"
person.FamilyName = "family_name"
person.MiddleNames = ("middle", "names")
person.PrefixTitles = ("prefix", "titles")
person.SuffixTitles = ("suffix", "titles")
subject().set_person(person)
subject().import_attributes()
assert subject().export_attributes() == {
"Identification": "identification",
"GivenName": "given_name",
"FamilyName": "family_name",
"MiddleNames": ["middle", "names"],
"PrefixTitles": ["prefix", "titles"],
"SuffixTitles": ["suffix", "titles"],
}
def test_getting_empty_list_attributes_as_none(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
result = subject().export_attributes()
assert result["MiddleNames"] is None
assert result["PrefixTitles"] is None
assert result["SuffixTitles"] is None
class TestGetPerson(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
person = ifc.createIfcPerson()
subject().set_person(person)
assert subject().get_person() == person
class TestAddAttribute(test.bim.bootstrap.NewFile):
def test_run(self):
subject().add_attribute("MiddleNames")
subject().add_attribute("PrefixTitles")
subject().add_attribute("SuffixTitles")
props = bpy.context.scene.BIMOwnerProperties
assert len(props.middle_names) == 1
assert len(props.prefix_titles) == 1
assert len(props.suffix_titles) == 1
class TestRemoveAttribute(test.bim.bootstrap.NewFile):
def test_run(self):
subject().add_attribute("MiddleNames")
subject().remove_attribute("MiddleNames", 0)
subject().add_attribute("PrefixTitles")
subject().remove_attribute("PrefixTitles", 0)
subject().add_attribute("SuffixTitles")
subject().remove_attribute("SuffixTitles", 0)
props = bpy.context.scene.BIMOwnerProperties
assert len(props.middle_names) == 0
assert len(props.prefix_titles) == 0
assert len(props.suffix_titles) == 0
@@ -0,0 +1,98 @@
# 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.role_editor import RoleEditor as subject
class TestImplementsTool(test.bim.bootstrap.NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.role_editor.RoleEditor)
class TestSetRole(test.bim.bootstrap.NewFile):
def test_run(self):
role = ifcopenshell.file().createIfcActorRole()
subject().set_role(role)
assert bpy.context.scene.BIMOwnerProperties.active_role_id == role.id()
class TestImportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
role.Role = "USERDEFINED"
role.UserDefinedRole = "UserDefinedRole"
role.Description = "Description"
subject().set_role(role)
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.role_attributes.get("Role").enum_value == "USERDEFINED"
assert props.role_attributes.get("UserDefinedRole").string_value == "UserDefinedRole"
assert props.role_attributes.get("Description").string_value == "Description"
def test_importing_twice(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
role.Role = "USERDEFINED"
subject().set_role(role)
subject().import_attributes()
role.Role = "ARCHITECT"
subject().import_attributes()
props = bpy.context.scene.BIMOwnerProperties
assert props.role_attributes.get("Role").enum_value == "ARCHITECT"
class TestClearRole(test.bim.bootstrap.NewFile):
def test_run(self):
role = ifcopenshell.file().createIfcActorRole()
subject().set_role(role)
subject().clear_role()
assert bpy.context.scene.BIMOwnerProperties.active_role_id == 0
class TestGetRole(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
subject().set_role(role)
assert subject().get_role() == role
class TestExportAttributes(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
role = ifc.createIfcActorRole()
role.Role = "USERDEFINED"
role.UserDefinedRole = "UserDefinedRole"
role.Description = "Description"
subject().set_role(role)
subject().import_attributes()
assert subject().export_attributes() == {
"Role": "USERDEFINED",
"UserDefinedRole": "UserDefinedRole",
"Description": "Description",
}