This commit is contained in:
Andrej730
2024-11-06 11:47:17 +05:00
parent 4e070545ae
commit 4fdb313449
9 changed files with 232 additions and 155 deletions
+1 -1
View File
@@ -171,7 +171,7 @@ class BIM_OT_multiple_file_selector(bpy.types.Operator):
filepath: bpy.props.StringProperty(subtype="FILE_PATH") filepath: bpy.props.StringProperty(subtype="FILE_PATH")
@classmethod @classmethod
def poll(self, context): def poll(cls, context):
return getattr(context, "file_props", None) is not None return getattr(context, "file_props", None) is not None
def execute(self, context): def execute(self, context):
+1
View File
@@ -434,6 +434,7 @@ class BIM_PT_tabs(Panel):
"QUALITY", "QUALITY",
"SWITCH", "SWITCH",
]: ]:
# Draw a little underscore below the active tab icon.
if aprops.tab == tab: if aprops.tab == tab:
row.prop(aprops, "active_tab", text="", icon="BLANK1") row.prop(aprops, "active_tab", text="", icon="BLANK1")
else: else:
+38 -18
View File
@@ -20,6 +20,9 @@ import os
import re import re
import csv import csv
import importlib import importlib
from pathlib import Path
from collections import defaultdict
from typing import Literal, Union, Any, Callable
try: try:
from openpyxl import Workbook from openpyxl import Workbook
@@ -44,12 +47,21 @@ except:
__version__ = version = "0.0.0" __version__ = version = "0.0.0"
ParserPreset = Literal["basic", "cobie24", "cobie24legacy"]
class Parser: class Parser:
def __init__(self, preset="basic"): config: dict[str, Any]
categories: defaultdict[str, dict[str, Any]]
get_custom_element_data: dict[
str, Union[Callable[[ifcopenshell.file, ifcopenshell.entity_instance], dict[str, Any]], dict[str, Any]]
]
duplicate_keys: list[tuple[dict[str, Any], dict[str, Any]]]
def __init__(self, preset: Union[ParserPreset, dict[str, Any]] = "basic"):
self.file = None self.file = None
self.preset = preset self.preset = preset
self.categories = {} self.categories = defaultdict(dict)
self.config = None
self.get_custom_element_data = {} self.get_custom_element_data = {}
self.duplicate_keys = [] self.duplicate_keys = []
@@ -59,9 +71,9 @@ class Parser:
else: else:
self.config = preset self.config = preset
def parse(self, ifc_file, name=None): # TODO: name is unused?
def parse(self, ifc_file: ifcopenshell.file, name=None):
for category_name, category_config in self.config["categories"].items(): for category_name, category_config in self.config["categories"].items():
self.categories.setdefault(category_name, {})
for element in category_config["get_category_elements"](ifc_file): for element in category_config["get_category_elements"](ifc_file):
get_element_data = category_config["get_element_data"] get_element_data = category_config["get_element_data"]
@@ -69,7 +81,7 @@ class Parser:
data = {} data = {}
for key, query in get_element_data.items(): for key, query in get_element_data.items():
data[key] = ifcopenshell.util.selector.get_element_value(element, query) data[key] = ifcopenshell.util.selector.get_element_value(element, query)
else: elif isinstance(get_element_data, Callable):
data = get_element_data(ifc_file, element) or {} data = get_element_data(ifc_file, element) or {}
get_custom_element_data = self.get_custom_element_data.get(category_name, lambda x, y: None) get_custom_element_data = self.get_custom_element_data.get(category_name, lambda x, y: None)
@@ -77,28 +89,26 @@ class Parser:
custom_data = {} custom_data = {}
for key, query in get_custom_element_data.items(): for key, query in get_custom_element_data.items():
custom_data[key] = ifcopenshell.util.selector.get_element_value(element, query) custom_data[key] = ifcopenshell.util.selector.get_element_value(element, query)
else: elif isinstance(get_custom_element_data, Callable):
custom_data = get_custom_element_data(ifc_file, element) or {} custom_data = get_custom_element_data(ifc_file, element) or {}
data.update(custom_data) data.update(custom_data)
if data: if data:
key = "-".join([str(data[k]) for k in category_config["keys"]]) key = "-".join([str(data[k]) for k in category_config["keys"]])
# TODO: duplicate_keys are never used?
if key in self.categories[category_name]: if key in self.categories[category_name]:
self.duplicate_keys.append((self.categories[category_name][key], data)) self.duplicate_keys.append((self.categories[category_name][key], data))
self.categories[category_name][key] = data self.categories[category_name][key] = data
def federate(self, paths): def federate(self, paths: list[str]) -> None:
for path in paths: for path in paths:
spreadsheet = pd.ExcelFile(path) spreadsheet = pd.ExcelFile(path)
sheet_names = spreadsheet.sheet_names sheet_names = spreadsheet.sheet_names
for category_name, category_config in self.config["categories"].items(): for category_name, category_config in self.config["categories"].items():
self.categories.setdefault(category_name, {})
if category_name not in sheet_names: if category_name not in sheet_names:
continue continue
self.categories.setdefault(category_name, {})
df = pd.read_excel(spreadsheet, sheet_name=category_name, keep_default_na=False) df = pd.read_excel(spreadsheet, sheet_name=category_name, keep_default_na=False)
for _, row in df.iterrows(): for _, row in df.iterrows():
key = "-".join([str(row[k]) for k in category_config["keys"]]) key = "-".join([str(row[k]) for k in category_config["keys"]])
@@ -106,18 +116,21 @@ class Parser:
continue continue
self.categories[category_name][key] = row.to_dict() self.categories[category_name][key] = row.to_dict()
def exclude_categories(self, names): def exclude_categories(self, names: list[str]) -> None:
for name in names: for name in names:
if name in self.config["categories"]: if name in self.config["categories"]:
del self.config["categories"][name] del self.config["categories"][name]
def exclude_element_data(self, category, names): def exclude_element_data(self, category: str, names: list[str]) -> None:
headers = self.config["categories"][category]["headers"] headers = self.config["categories"][category]["headers"]
self.config["categories"][category]["headers"] = [h for h in headers if h not in names] self.config["categories"][category]["headers"] = [h for h in headers if h not in names]
class Writer: class Writer:
def __init__(self, parser): config: dict[str, Any]
categories: dict[str, dict[str, Any]]
def __init__(self, parser: Parser):
self.parser = parser self.parser = parser
if isinstance(self.parser.preset, str): if isinstance(self.parser.preset, str):
module = importlib.import_module(f"ifcfm.{self.parser.preset}") module = importlib.import_module(f"ifcfm.{self.parser.preset}")
@@ -127,7 +140,14 @@ class Writer:
else: else:
self.config = getattr(self.parser.preset, "config") self.config = getattr(self.parser.preset, "config")
def write(self, null="N/A", empty="-", bool_true="YES", bool_false="NO", list_separator=", "): def write(
self,
null: str = "N/A",
empty: str = "-",
bool_true: str = "YES",
bool_false: str = "NO",
list_separator: str = ", ",
) -> None:
self.categories = {} self.categories = {}
null = self.config.get("null", null) null = self.config.get("null", null)
empty = self.config.get("empty", empty) empty = self.config.get("empty", empty)
@@ -191,7 +211,7 @@ class Writer:
for row in data["rows"]: for row in data["rows"]:
writer.writerow(row) writer.writerow(row)
def write_ods(self, output): def write_ods(self, output: str) -> None:
doc = OpenDocumentSpreadsheet() doc = OpenDocumentSpreadsheet()
for key, value in self.config.get("colours", {}).items(): for key, value in self.config.get("colours", {}).items():
@@ -229,7 +249,7 @@ class Writer:
doc.save(output, True) doc.save(output, True)
def write_xlsx(self, output): def write_xlsx(self, output: str) -> None:
workbook = Workbook() workbook = Workbook()
cell_formats = {} cell_formats = {}
@@ -270,7 +290,7 @@ class Writer:
workbook.save(output) workbook.save(output)
def write_pd(self): def write_pd(self) -> dict[str, pd.DataFrame]:
results = {} results = {}
for category, data in self.categories.items(): for category, data in self.categories.items():
results[category] = pd.DataFrame(data["rows"], columns=data["headers"]) results[category] = pd.DataFrame(data["rows"], columns=data["headers"])
+18
View File
@@ -1,3 +1,21 @@
# IfcFM - IFC for facility management
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcFM.
#
# IfcFM is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcFM 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcFM. If not, see <http://www.gnu.org/licenses/>.
import ifcfm import ifcfm
import argparse import argparse
import ifcopenshell import ifcopenshell
+31 -25
View File
@@ -17,26 +17,28 @@
# along with IfcFM. If not, see <http://www.gnu.org/licenses/>. # along with IfcFM. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell import ifcopenshell
import ifcopenshell.util.fm
import ifcopenshell.util.date
import ifcopenshell.util.system
import ifcopenshell.util.placement
import ifcopenshell.util.classification import ifcopenshell.util.classification
import ifcopenshell.util.date
import ifcopenshell.util.element
import ifcopenshell.util.fm
import ifcopenshell.util.placement
import ifcopenshell.util.system
from typing import Any, Union, Optional
def get_facilities(ifc_file): def get_facilities(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcBuilding") return ifc_file.by_type("IfcBuilding")
def get_storeys(ifc_file): def get_storeys(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcBuildingStorey") return ifc_file.by_type("IfcBuildingStorey")
def get_spaces(ifc_file): def get_spaces(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcSpace") return ifc_file.by_type("IfcSpace")
def get_zones(ifc_file): def get_zones(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
zones = [] zones = []
for zone in ifc_file.by_type("IfcZone"): for zone in ifc_file.by_type("IfcZone"):
for rel in zone.IsGroupedBy: for rel in zone.IsGroupedBy:
@@ -44,22 +46,22 @@ def get_zones(ifc_file):
return zones return zones
def get_element_types(ifc_file): def get_element_types(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifcopenshell.util.fm.get_fmhem_types(ifc_file) return ifcopenshell.util.fm.get_fmhem_types(ifc_file)
def get_elements(ifc_file): def get_elements(ifc_file: ifcopenshell.file) -> set[ifcopenshell.entity_instance]:
elements = set() elements = set()
for element_type in get_element_types(ifc_file): for element_type in get_element_types(ifc_file):
elements.update(ifcopenshell.util.element.get_types(element_type)) elements.update(ifcopenshell.util.element.get_types(element_type))
return elements return elements
def get_systems(ifc_file): def get_systems(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcSystem") return ifc_file.by_type("IfcSystem")
def get_facility_data(ifc_file, element): def get_facility_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
return { return {
"Name": element.Name, "Name": element.Name,
"ProjectName": ifc_file.by_type("IfcProject")[0].Name, "ProjectName": ifc_file.by_type("IfcProject")[0].Name,
@@ -78,7 +80,7 @@ def get_facility_data(ifc_file, element):
} }
def get_storey_data(ifc_file, element): def get_storey_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
return { return {
"Name": element.Name, "Name": element.Name,
"ClassificationIdentification": "Level", "ClassificationIdentification": "Level",
@@ -92,7 +94,7 @@ def get_storey_data(ifc_file, element):
} }
def get_space_data(ifc_file, element): def get_space_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
psets = ifcopenshell.util.element.get_psets(element) psets = ifcopenshell.util.element.get_psets(element)
return { return {
"Name": element.Name, "Name": element.Name,
@@ -110,7 +112,7 @@ def get_space_data(ifc_file, element):
} }
def get_zone_data(ifc_file, element): def get_zone_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
zone, space = element zone, space = element
return { return {
"Name": zone.Name, "Name": zone.Name,
@@ -122,7 +124,7 @@ def get_zone_data(ifc_file, element):
} }
def get_element_type_data(ifc_file, element): def get_element_type_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
psets = ifcopenshell.util.element.get_psets(element) psets = ifcopenshell.util.element.get_psets(element)
return { return {
"Name": element.Name, "Name": element.Name,
@@ -143,7 +145,7 @@ def get_element_type_data(ifc_file, element):
} }
def get_element_data(ifc_file, element): def get_element_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
space = ifcopenshell.util.element.get_container(element) space = ifcopenshell.util.element.get_container(element)
space_name = space.Name if space.is_a("IfcSpace") else None space_name = space.Name if space.is_a("IfcSpace") else None
systems = ifcopenshell.util.system.get_element_systems(element) systems = ifcopenshell.util.system.get_element_systems(element)
@@ -170,7 +172,7 @@ def get_element_data(ifc_file, element):
} }
def get_system_data(ifc_file, element): def get_system_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
return { return {
"Name": element.Name, "Name": element.Name,
"Description": element.Description, "Description": element.Description,
@@ -183,25 +185,27 @@ def get_system_data(ifc_file, element):
} }
def get_owner_name(element): def get_owner_name(element: ifcopenshell.entity_instance) -> Union[str, None]:
if not getattr(element, "OwnerHistory", None): if not getattr(element, "OwnerHistory", None):
return return
return element.OwnerHistory.OwningUser.TheOrganization.Name return element.OwnerHistory.OwningUser.TheOrganization.Name
def get_owner_creation_date(element): def get_owner_creation_date(element: ifcopenshell.entity_instance) -> Union[str, None]:
if not getattr(element, "OwnerHistory", None): if not getattr(element, "OwnerHistory", None):
return return
return ifcopenshell.util.date.ifc2datetime(element.OwnerHistory.CreationDate).isoformat() return ifcopenshell.util.date.ifc2datetime(element.OwnerHistory.CreationDate).isoformat()
def get_owner_application(element): def get_owner_application(element: ifcopenshell.entity_instance) -> Union[str, None]:
if not getattr(element, "OwnerHistory", None): if not getattr(element, "OwnerHistory", None):
return return
return element.OwnerHistory.OwningApplication.ApplicationFullName return element.OwnerHistory.OwningApplication.ApplicationFullName
def get_facility_parent(element, ifc_class): def get_facility_parent(
element: ifcopenshell.entity_instance, ifc_class: str
) -> Union[ifcopenshell.entity_instance, None]:
parent = ifcopenshell.util.element.get_aggregate(element) parent = ifcopenshell.util.element.get_aggregate(element)
while parent: while parent:
if parent.is_a(ifc_class): if parent.is_a(ifc_class):
@@ -211,7 +215,7 @@ def get_facility_parent(element, ifc_class):
parent = ifcopenshell.util.element.get_aggregate(parent) parent = ifcopenshell.util.element.get_aggregate(parent)
def get_classification_identification(element): def get_classification_identification(element: ifcopenshell.entity_instance) -> Union[str, None]:
references = list(ifcopenshell.util.classification.get_references(element)) references = list(ifcopenshell.util.classification.get_references(element))
if references: if references:
if hasattr(references[0], "Identification"): if hasattr(references[0], "Identification"):
@@ -219,13 +223,15 @@ def get_classification_identification(element):
return references[0].ItemReference return references[0].ItemReference
def get_classification_name(element): def get_classification_name(element: ifcopenshell.entity_instance) -> Union[str, None]:
references = list(ifcopenshell.util.classification.get_references(element)) references = list(ifcopenshell.util.classification.get_references(element))
if references: if references:
return references[0].Name return references[0].Name
def get_property(psets, pset_name, prop_name, decimals=None): def get_property(
psets: dict[str, Any], pset_name: str, prop_name: str, decimals: Optional[int] = None
) -> Union[Any, None, float]:
if pset_name in psets: if pset_name in psets:
result = psets[pset_name].get(prop_name, None) result = psets[pset_name].get(prop_name, None)
if decimals is None or result is None: if decimals is None or result is None:
+67 -52
View File
@@ -17,11 +17,13 @@
# along with IfcFM. If not, see <http://www.gnu.org/licenses/>. # along with IfcFM. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell import ifcopenshell
import ifcopenshell.util.fm
import ifcopenshell.util.date
import ifcopenshell.util.system
import ifcopenshell.util.placement
import ifcopenshell.util.classification import ifcopenshell.util.classification
import ifcopenshell.util.date
import ifcopenshell.util.element
import ifcopenshell.util.fm
import ifcopenshell.util.placement
import ifcopenshell.util.system
from typing import Any, Union, Optional
# The original BIMServer plugin has a function called ifcToCOBie: # The original BIMServer plugin has a function called ifcToCOBie:
@@ -34,15 +36,15 @@ import ifcopenshell.util.classification
# Impact, Coordinate, Issue, Picklist # Impact, Coordinate, Issue, Picklist
def get_contacts(ifc_file): def get_contacts(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcActor") return ifc_file.by_type("IfcActor")
def get_facilities(ifc_file): def get_facilities(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcBuilding") return ifc_file.by_type("IfcBuilding")
def get_floors(ifc_file): def get_floors(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return [ return [
e e
for e in ifc_file.by_type("IfcBuildingStorey") for e in ifc_file.by_type("IfcBuildingStorey")
@@ -50,11 +52,11 @@ def get_floors(ifc_file):
] ]
def get_spaces(ifc_file): def get_spaces(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcSpace") return ifc_file.by_type("IfcSpace")
def get_zones(ifc_file): def get_zones(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
results = [] results = []
zones = ifc_file.by_type("IfcZone") zones = ifc_file.by_type("IfcZone")
for zone in zones or []: for zone in zones or []:
@@ -69,18 +71,18 @@ def get_zones(ifc_file):
return results return results
def get_types(ifc_file): def get_types(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifcopenshell.util.fm.get_cobie_types(ifc_file) return ifcopenshell.util.fm.get_cobie_types(ifc_file)
def get_components(ifc_file): def get_components(ifc_file: ifcopenshell.file) -> set[ifcopenshell.entity_instance]:
elements = set() elements = set()
for element_type in get_types(ifc_file): for element_type in get_types(ifc_file):
elements.update(ifcopenshell.util.element.get_types(element_type)) elements.update(ifcopenshell.util.element.get_types(element_type))
return elements return elements
def get_systems(ifc_file): def get_systems(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
results = [] results = []
components = get_components(ifc_file) components = get_components(ifc_file)
if ifc_file.schema == "IFC2X3": if ifc_file.schema == "IFC2X3":
@@ -94,7 +96,7 @@ def get_systems(ifc_file):
return results return results
def get_assemblies(ifc_file): def get_assemblies(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
results = [] results = []
layer_sets = ifc_file.by_type("IfcMaterialLayerSet") layer_sets = ifc_file.by_type("IfcMaterialLayerSet")
layer_sets = [] # This is temporarily overridden because it is unclear exactly how this is stored in Type. layer_sets = [] # This is temporarily overridden because it is unclear exactly how this is stored in Type.
@@ -128,23 +130,25 @@ def get_assemblies(ifc_file):
return results return results
def get_connections(ifc_file): def get_connections(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcRelConnectsPorts") return ifc_file.by_type("IfcRelConnectsPorts")
def get_spares(ifc_file): def get_spares(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcConstructionProductResource") return ifc_file.by_type("IfcConstructionProductResource")
def get_resources(ifc_file): def get_resources(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcConstructionEquipmentResource") return ifc_file.by_type("IfcConstructionEquipmentResource")
def get_jobs(ifc_file): def get_jobs(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcTask") return ifc_file.by_type("IfcTask")
def get_documents(ifc_file): def get_documents(
ifc_file: ifcopenshell.file,
) -> list[tuple[ifcopenshell.entity_instance, ifcopenshell.entity_instance, ifcopenshell.entity_instance]]:
# The original COBie-Plugins assumes a single related object per rel. I think this was wrong. # The original COBie-Plugins assumes a single related object per rel. I think this was wrong.
results = [] results = []
for rel in ifc_file.by_type("IfcRelAssociatesDocument"): for rel in ifc_file.by_type("IfcRelAssociatesDocument"):
@@ -158,7 +162,7 @@ def get_documents(ifc_file):
return results return results
def get_attributes(ifc_file): def get_attributes(ifc_file: ifcopenshell.file) -> list[dict[str, Any]]:
results = [] results = []
history = get_history(ifc_file) history = get_history(ifc_file)
created_by = get_email_from_history(history) if history else None created_by = get_email_from_history(history) if history else None
@@ -247,7 +251,7 @@ def get_attributes(ifc_file):
return results return results
def get_contact_data(ifc_file, element): def get_contact_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
the_actor = element.TheActor the_actor = element.TheActor
if the_actor.is_a("IfcPerson"): if the_actor.is_a("IfcPerson"):
@@ -299,7 +303,7 @@ def get_contact_data(ifc_file, element):
} }
def get_facility_data(ifc_file, element): def get_facility_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
site = get_facility_parent(element, "IfcSite") site = get_facility_parent(element, "IfcSite")
site_name = None site_name = None
site_description = None site_description = None
@@ -343,7 +347,7 @@ def get_facility_data(ifc_file, element):
} }
def get_floor_data(ifc_file, element): def get_floor_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
height_names = { height_names = {
"Height", "Height",
"NetHeight", "NetHeight",
@@ -382,7 +386,7 @@ def get_floor_data(ifc_file, element):
} }
def get_space_data(ifc_file, element): def get_space_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
floor_name = None floor_name = None
floor = ifcopenshell.util.element.get_aggregate(element) floor = ifcopenshell.util.element.get_aggregate(element)
if floor and floor.is_a("IfcBuildingStorey"): if floor and floor.is_a("IfcBuildingStorey"):
@@ -425,7 +429,7 @@ def get_space_data(ifc_file, element):
} }
def get_zone_data(ifc_file, element): def get_zone_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
zone, space = element zone, space = element
name = zone.Name name = zone.Name
@@ -449,7 +453,7 @@ def get_zone_data(ifc_file, element):
} }
def get_type_data(ifc_file, element): def get_type_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
asset_type = None asset_type = None
manufacturer = None manufacturer = None
model_number = None model_number = None
@@ -555,7 +559,7 @@ def get_type_data(ifc_file, element):
} }
def get_component_data(ifc_file, element): def get_component_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
space = ifcopenshell.util.element.get_container(element) space = ifcopenshell.util.element.get_container(element)
space_name = space.Name if space.is_a("IfcSpace") else None space_name = space.Name if space.is_a("IfcSpace") else None
systems = ifcopenshell.util.system.get_element_systems(element) systems = ifcopenshell.util.system.get_element_systems(element)
@@ -610,7 +614,7 @@ def get_component_data(ifc_file, element):
} }
def get_system_data(ifc_file, element): def get_system_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
system, component = element system, component = element
category = get_category(system) category = get_category(system)
component_name = val(component.Name) component_name = val(component.Name)
@@ -627,7 +631,7 @@ def get_system_data(ifc_file, element):
} }
def get_assembly_data(ifc_file, element): def get_assembly_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
rel, relating_object, related_object = element rel, relating_object, related_object = element
if relating_object.is_a("IfcMaterialLayerSet"): if relating_object.is_a("IfcMaterialLayerSet"):
@@ -663,7 +667,7 @@ def get_assembly_data(ifc_file, element):
} }
def get_connection_data(ifc_file, element): def get_connection_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
connection_type = ( connection_type = (
val(element.RelatingPort.ObjectType) val(element.RelatingPort.ObjectType)
or val(element.RelatedPort.ObjectType) or val(element.RelatedPort.ObjectType)
@@ -691,7 +695,7 @@ def get_connection_data(ifc_file, element):
} }
def get_spare_data(ifc_file, element): def get_spare_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
type_name = None type_name = None
for rel in element.ResourceOf or []: for rel in element.ResourceOf or []:
for related_object in rel.RelatedObjects or []: for related_object in rel.RelatedObjects or []:
@@ -726,7 +730,7 @@ def get_spare_data(ifc_file, element):
} }
def get_resource_data(ifc_file, element): def get_resource_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
return { return {
"Name": val(element.Name), "Name": val(element.Name),
"CreatedBy": get_created_by(element), "CreatedBy": get_created_by(element),
@@ -739,7 +743,7 @@ def get_resource_data(ifc_file, element):
} }
def get_job_data(ifc_file, element): def get_job_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
type_names = [] type_names = []
resource_names = [] resource_names = []
for rel in element.OperatesOn or []: for rel in element.OperatesOn or []:
@@ -810,7 +814,7 @@ def get_job_data(ifc_file, element):
} }
def get_document_data(ifc_file, element): def get_document_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
rel, doc, related_object = element rel, doc, related_object = element
directory = getattr(doc, "Location", None) directory = getattr(doc, "Location", None)
file = None file = None
@@ -847,11 +851,13 @@ def get_document_data(ifc_file, element):
} }
def get_attribute_data(ifc_file, element): def get_attribute_data(
ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
return element return element
def get_unit_type_name(ifc_file, unit_type): def get_unit_type_name(ifc_file: ifcopenshell.file, unit_type: str) -> Union[str, None]:
for unit in ifc_file.by_type("IfcUnitAssignment")[0].Units: for unit in ifc_file.by_type("IfcUnitAssignment")[0].Units:
if unit.is_a("IfcNamedUnit") and unit.UnitType == unit_type: if unit.is_a("IfcNamedUnit") and unit.UnitType == unit_type:
if unit.is_a("IfcSIUnit"): if unit.is_a("IfcSIUnit"):
@@ -868,17 +874,17 @@ def get_unit_type_name(ifc_file, unit_type):
return val(unit.Currency) return val(unit.Currency)
def get_unit_name(ifc_file, unit): def get_unit_name(ifc_file: ifcopenshell.entity_instance, unit: ifcopenshell.entity_instance) -> Union[str, None]:
if unit.is_a("IfcNamedUnit"): if unit.is_a("IfcNamedUnit"):
return val(unit.Name) return val(unit.Name)
def get_created_by(element): def get_created_by(element: ifcopenshell.entity_instance) -> Union[str, None]:
if getattr(element, "OwnerHistory", None): if getattr(element, "OwnerHistory", None):
return get_email_from_history(element.OwnerHistory) return get_email_from_history(element.OwnerHistory)
def get_email_from_history(element): def get_email_from_history(element: ifcopenshell.entity_instance) -> Union[str, None]:
pao = element.OwningUser pao = element.OwningUser
if pao.is_a("IfcPersonAndOrganization"): if pao.is_a("IfcPersonAndOrganization"):
return get_email_from_pao(pao.ThePerson, pao.TheOrganization) return get_email_from_pao(pao.ThePerson, pao.TheOrganization)
@@ -888,7 +894,9 @@ def get_email_from_history(element):
return get_email_from_pao(None, pao) return get_email_from_pao(None, pao)
def get_email_from_pao(person, organization): def get_email_from_pao(
person: ifcopenshell.entity_instance, organization: ifcopenshell.entity_instance
) -> Union[str, None]:
if organization: if organization:
for address in organization.Addresses or []: for address in organization.Addresses or []:
if address.is_a("IfcTelecomAddress") and address.ElectronicMailAddresses: if address.is_a("IfcTelecomAddress") and address.ElectronicMailAddresses:
@@ -900,23 +908,25 @@ def get_email_from_pao(person, organization):
return address.ElectronicMailAddresses[0] return address.ElectronicMailAddresses[0]
def get_owner_name(element): def get_owner_name(element: ifcopenshell.entity_instance) -> Union[str, None]:
if getattr(element, "OwnerHistory", None): if getattr(element, "OwnerHistory", None):
return element.OwnerHistory.OwningUser.TheOrganization.Name return element.OwnerHistory.OwningUser.TheOrganization.Name
def get_created_on(element): def get_created_on(element: ifcopenshell.entity_instance) -> str:
if getattr(element, "OwnerHistory", None): if getattr(element, "OwnerHistory", None):
return ifcopenshell.util.date.ifc2datetime(element.OwnerHistory.CreationDate).isoformat() return ifcopenshell.util.date.ifc2datetime(element.OwnerHistory.CreationDate).isoformat()
return "1900-12-31T23:59:59" # Yes, really return "1900-12-31T23:59:59" # Yes, really
def get_external_system(element): def get_external_system(element: ifcopenshell.entity_instance) -> Union[str, None]:
if getattr(element, "OwnerHistory", None): if getattr(element, "OwnerHistory", None):
return val(element.OwnerHistory.OwningApplication.ApplicationFullName) return val(element.OwnerHistory.OwningApplication.ApplicationFullName)
def get_facility_parent(element, ifc_class): def get_facility_parent(
element: ifcopenshell.entity_instance, ifc_class: str
) -> Union[ifcopenshell.entity_instance, None]:
parent = ifcopenshell.util.element.get_aggregate(element) parent = ifcopenshell.util.element.get_aggregate(element)
while parent: while parent:
if parent.is_a(ifc_class): if parent.is_a(ifc_class):
@@ -926,11 +936,11 @@ def get_facility_parent(element, ifc_class):
parent = ifcopenshell.util.element.get_aggregate(parent) parent = ifcopenshell.util.element.get_aggregate(parent)
def val(x): def val(x: Any) -> Any:
return x if x not in ("", "n/a") else None return x if x not in ("", "n/a") else None
def get_area_measurement(element): def get_area_measurement(element: ifcopenshell.entity_instance) -> Union[str, None]:
for relationship in getattr(element, "IsDefinedBy", []) or []: for relationship in getattr(element, "IsDefinedBy", []) or []:
if relationship.is_a("IfcRelDefinesByProperties"): if relationship.is_a("IfcRelDefinesByProperties"):
definition = relationship.RelatingPropertyDefinition definition = relationship.RelatingPropertyDefinition
@@ -950,7 +960,7 @@ def get_area_measurement(element):
return value return value
def get_category(element): def get_category(element: ifcopenshell.entity_instance) -> Union[str, None]:
references = list(ifcopenshell.util.classification.get_references(element)) references = list(ifcopenshell.util.classification.get_references(element))
results = [] results = []
for reference in references: for reference in references:
@@ -968,7 +978,9 @@ def get_category(element):
return ",".join(results) return ",".join(results)
def get_pao_address(person, organization, name): def get_pao_address(
person: ifcopenshell.entity_instance, organization: ifcopenshell.entity_instance, name: str
) -> Union[str, None]:
for actor in [organization, person]: for actor in [organization, person]:
if not actor: if not actor:
continue continue
@@ -982,7 +994,9 @@ def get_pao_address(person, organization, name):
return result return result
def get_property(psets, pset_name, prop_name, decimals=None): def get_property(
psets: dict[str, dict[str, Any]], pset_name: str, prop_name: str, decimals: Optional[int] = None
) -> Union[Any, None, float]:
if pset_name in psets: if pset_name in psets:
result = psets[pset_name].get(prop_name, None) result = psets[pset_name].get(prop_name, None)
if decimals is None or result is None: if decimals is None or result is None:
@@ -990,13 +1004,13 @@ def get_property(psets, pset_name, prop_name, decimals=None):
return round(result, decimals) return round(result, decimals)
def get_history(ifc_file): def get_history(ifc_file: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None]:
histories = ifc_file.by_type("IfcOwnerHistory") histories = ifc_file.by_type("IfcOwnerHistory")
if histories: if histories:
return sorted(histories, key=lambda x: x.id())[-1] return sorted(histories, key=lambda x: x.id())[-1]
def get_property_unit(pset, prop_name): def get_property_unit(pset: ifcopenshell.entity_instance, prop_name: str) -> Union[str, None]:
for prop in getattr(pset, "HasProperties", []) or []: for prop in getattr(pset, "HasProperties", []) or []:
if prop.Name == prop_name: if prop.Name == prop_name:
unit = getattr(prop, "Unit", None) unit = getattr(prop, "Unit", None)
@@ -1004,7 +1018,8 @@ def get_property_unit(pset, prop_name):
return get_unit_name(unit) return get_unit_name(unit)
def get_allowed_values(pset_id, prop_name): # TODO: never used?
def get_allowed_values(ifc_file: ifcopenshell.file, pset_id: int, prop_name: str) -> Union[str, None]:
pset = ifc_file.by_id(pset_id) pset = ifc_file.by_id(pset_id)
for prop in getattr(pset, "HasProperties", []) or []: for prop in getattr(pset, "HasProperties", []) or []:
if prop.Name == prop_name: if prop.Name == prop_name:
@@ -1012,7 +1027,7 @@ def get_allowed_values(pset_id, prop_name):
return ",".join([v.wrappedValue for v in prop.EnumerationValues]) return ",".join([v.wrappedValue for v in prop.EnumerationValues])
def get_sheet_name(element): def get_sheet_name(element: ifcopenshell.entity_instance) -> Union[str, None]:
if element.is_a("IfcBuilding"): if element.is_a("IfcBuilding"):
return "Facility" return "Facility"
elif element.is_a("IfcBuildingStorey"): elif element.is_a("IfcBuildingStorey"):
+63 -52
View File
@@ -18,11 +18,13 @@
import ifcopenshell import ifcopenshell
import ifcopenshell.guid import ifcopenshell.guid
import ifcopenshell.util.fm
import ifcopenshell.util.date
import ifcopenshell.util.system
import ifcopenshell.util.placement
import ifcopenshell.util.classification import ifcopenshell.util.classification
import ifcopenshell.util.date
import ifcopenshell.util.element
import ifcopenshell.util.fm
import ifcopenshell.util.placement
import ifcopenshell.util.system
from typing import Any, Union, Optional
# The original BIMServer plugin has a function called ifcToCOBie: # The original BIMServer plugin has a function called ifcToCOBie:
@@ -35,15 +37,15 @@ import ifcopenshell.util.classification
# Impact, Coordinate, Issue, Picklist # Impact, Coordinate, Issue, Picklist
def get_contacts(ifc_file): def get_contacts(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcPersonAndOrganization") return ifc_file.by_type("IfcPersonAndOrganization")
def get_facilities(ifc_file): def get_facilities(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcBuilding") return ifc_file.by_type("IfcBuilding")
def get_floors(ifc_file): def get_floors(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return [ return [
e e
for e in ifc_file.by_type("IfcBuildingStorey") for e in ifc_file.by_type("IfcBuildingStorey")
@@ -51,11 +53,11 @@ def get_floors(ifc_file):
] ]
def get_spaces(ifc_file): def get_spaces(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcSpace") return ifc_file.by_type("IfcSpace")
def get_zones(ifc_file): def get_zones(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
results = [] results = []
zones = ifc_file.by_type("IfcZone") zones = ifc_file.by_type("IfcZone")
for zone in zones or []: for zone in zones or []:
@@ -86,18 +88,18 @@ def get_zones(ifc_file):
return results return results
def get_types(ifc_file): def get_types(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifcopenshell.util.fm.get_cobie_types(ifc_file) return ifcopenshell.util.fm.get_cobie_types(ifc_file)
def get_components(ifc_file): def get_components(ifc_file: ifcopenshell.file) -> set[ifcopenshell.entity_instance]:
elements = set() elements = set()
for element_type in get_types(ifc_file): for element_type in get_types(ifc_file):
elements.update(ifcopenshell.util.element.get_types(element_type)) elements.update(ifcopenshell.util.element.get_types(element_type))
return elements return elements
def get_systems(ifc_file): def get_systems(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
results = [] results = []
components = get_components(ifc_file) components = get_components(ifc_file)
if ifc_file.schema == "IFC2X3": if ifc_file.schema == "IFC2X3":
@@ -111,7 +113,7 @@ def get_systems(ifc_file):
return results return results
def get_assemblies(ifc_file): def get_assemblies(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
results = [] results = []
layer_sets = ifc_file.by_type("IfcMaterialLayerSet") layer_sets = ifc_file.by_type("IfcMaterialLayerSet")
layer_sets = [] # This is temporarily overridden because it is unclear exactly how this is stored in Type. layer_sets = [] # This is temporarily overridden because it is unclear exactly how this is stored in Type.
@@ -145,23 +147,25 @@ def get_assemblies(ifc_file):
return results return results
def get_connections(ifc_file): def get_connections(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcRelConnectsPorts") return ifc_file.by_type("IfcRelConnectsPorts")
def get_spares(ifc_file): def get_spares(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcConstructionProductResource") return ifc_file.by_type("IfcConstructionProductResource")
def get_resources(ifc_file): def get_resources(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcConstructionEquipmentResource") return ifc_file.by_type("IfcConstructionEquipmentResource")
def get_jobs(ifc_file): def get_jobs(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
return ifc_file.by_type("IfcTask") return ifc_file.by_type("IfcTask")
def get_documents(ifc_file): def get_documents(
ifc_file: ifcopenshell.file,
) -> list[tuple[ifcopenshell.entity_instance, ifcopenshell.entity_instance, ifcopenshell.entity_instance]]:
# The original COBie-Plugins assumes a single related object per rel. I think this was wrong. # The original COBie-Plugins assumes a single related object per rel. I think this was wrong.
results = [] results = []
for rel in ifc_file.by_type("IfcRelAssociatesDocument"): for rel in ifc_file.by_type("IfcRelAssociatesDocument"):
@@ -175,7 +179,7 @@ def get_documents(ifc_file):
return results return results
def get_attributes(ifc_file): def get_attributes(ifc_file: ifcopenshell.file) -> list[dict[str, Any]]:
results = [] results = []
history = get_history(ifc_file) history = get_history(ifc_file)
created_by = get_email_from_history(history) if history else None created_by = get_email_from_history(history) if history else None
@@ -265,7 +269,7 @@ def get_attributes(ifc_file):
return results return results
def get_contact_data(ifc_file, element): def get_contact_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
email = get_email_from_pao(element) email = get_email_from_pao(element)
history = get_history(ifc_file) history = get_history(ifc_file)
@@ -314,7 +318,7 @@ def get_contact_data(ifc_file, element):
} }
def get_facility_data(ifc_file, element): def get_facility_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
site = get_facility_parent(element, "IfcSite") site = get_facility_parent(element, "IfcSite")
site_name = None site_name = None
site_description = None site_description = None
@@ -365,7 +369,7 @@ def get_facility_data(ifc_file, element):
} }
def get_floor_data(ifc_file, element): def get_floor_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
external_object = element.is_a() external_object = element.is_a()
if element.ObjectType and element.ObjectType.lower() in ("site", "ifcsite"): if element.ObjectType and element.ObjectType.lower() in ("site", "ifcsite"):
external_object = "IfcSite" external_object = "IfcSite"
@@ -409,7 +413,7 @@ def get_floor_data(ifc_file, element):
} }
def get_space_data(ifc_file, element): def get_space_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
floor_name = None floor_name = None
floor = ifcopenshell.util.element.get_aggregate(element) floor = ifcopenshell.util.element.get_aggregate(element)
if floor and floor.is_a("IfcBuildingStorey"): if floor and floor.is_a("IfcBuildingStorey"):
@@ -452,7 +456,7 @@ def get_space_data(ifc_file, element):
} }
def get_zone_data(ifc_file, element): def get_zone_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
zone, space = element zone, space = element
if isinstance(zone, tuple): if isinstance(zone, tuple):
@@ -493,7 +497,7 @@ def get_zone_data(ifc_file, element):
} }
def get_type_data(ifc_file, element): def get_type_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
pset_metadata = {} pset_metadata = {}
pset_mapping = { pset_mapping = {
"manufacturer": {"Manufacturer"}, "manufacturer": {"Manufacturer"},
@@ -628,7 +632,7 @@ def get_type_data(ifc_file, element):
} }
def get_component_data(ifc_file, element): def get_component_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
space = ifcopenshell.util.element.get_container(element) space = ifcopenshell.util.element.get_container(element)
space_name = space.Name if space.is_a("IfcSpace") else None space_name = space.Name if space.is_a("IfcSpace") else None
systems = ifcopenshell.util.system.get_element_systems(element) systems = ifcopenshell.util.system.get_element_systems(element)
@@ -684,7 +688,7 @@ def get_component_data(ifc_file, element):
} }
def get_system_data(ifc_file, element): def get_system_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
system, component = element system, component = element
category = get_category(system) category = get_category(system)
component_name = val(component.Name) component_name = val(component.Name)
@@ -702,7 +706,7 @@ def get_system_data(ifc_file, element):
} }
def get_assembly_data(ifc_file, element): def get_assembly_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
rel, relating_object, related_object = element rel, relating_object, related_object = element
if relating_object.is_a("IfcMaterialLayerSet"): if relating_object.is_a("IfcMaterialLayerSet"):
@@ -739,7 +743,7 @@ def get_assembly_data(ifc_file, element):
} }
def get_connection_data(ifc_file, element): def get_connection_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
connection_type = ( connection_type = (
val(element.RelatingPort.ObjectType) val(element.RelatingPort.ObjectType)
or val(element.RelatedPort.ObjectType) or val(element.RelatedPort.ObjectType)
@@ -768,7 +772,7 @@ def get_connection_data(ifc_file, element):
} }
def get_spare_data(ifc_file, element): def get_spare_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
type_name = None type_name = None
for rel in element.ResourceOf or []: for rel in element.ResourceOf or []:
for related_object in rel.RelatedObjects or []: for related_object in rel.RelatedObjects or []:
@@ -804,7 +808,7 @@ def get_spare_data(ifc_file, element):
} }
def get_resource_data(ifc_file, element): def get_resource_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
return { return {
"key": val(element.Name), "key": val(element.Name),
"Name": val(element.Name), "Name": val(element.Name),
@@ -818,7 +822,7 @@ def get_resource_data(ifc_file, element):
} }
def get_job_data(ifc_file, element): def get_job_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
type_names = [] type_names = []
resource_names = [] resource_names = []
for rel in element.OperatesOn or []: for rel in element.OperatesOn or []:
@@ -890,7 +894,7 @@ def get_job_data(ifc_file, element):
} }
def get_document_data(ifc_file, element): def get_document_data(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> dict[str, Any]:
rel, doc, related_object = element rel, doc, related_object = element
directory = getattr(doc, "Location", None) directory = getattr(doc, "Location", None)
file = None file = None
@@ -928,11 +932,13 @@ def get_document_data(ifc_file, element):
} }
def get_attribute_data(ifc_file, element): def get_attribute_data(
ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
return element return element
def get_unit_type_name(ifc_file, unit_type): def get_unit_type_name(ifc_file: ifcopenshell.file, unit_type: str) -> Union[str, None]:
for unit in ifc_file.by_type("IfcUnitAssignment")[0].Units: for unit in ifc_file.by_type("IfcUnitAssignment")[0].Units:
if unit.is_a("IfcNamedUnit") and unit.UnitType == unit_type: if unit.is_a("IfcNamedUnit") and unit.UnitType == unit_type:
if unit.is_a("IfcSIUnit"): if unit.is_a("IfcSIUnit"):
@@ -949,23 +955,23 @@ def get_unit_type_name(ifc_file, unit_type):
return val(unit.Currency) return val(unit.Currency)
def get_unit_name(ifc_file, unit): def get_unit_name(ifc_file: ifcopenshell.entity_instance, unit: ifcopenshell.entity_instance) -> Union[str, None]:
if unit.is_a("IfcNamedUnit"): if unit.is_a("IfcNamedUnit"):
return val(unit.Name) return val(unit.Name)
def get_created_by(element): def get_created_by(element: ifcopenshell.entity_instance) -> Union[str, None]:
if getattr(element, "OwnerHistory", None): if getattr(element, "OwnerHistory", None):
return get_email_from_history(element.OwnerHistory) return get_email_from_history(element.OwnerHistory)
def get_email_from_history(element): def get_email_from_history(element: ifcopenshell.entity_instance) -> Union[str, None]:
pao = element.OwningUser pao = element.OwningUser
if pao.is_a("IfcPersonAndOrganization"): if pao.is_a("IfcPersonAndOrganization"):
return get_email_from_pao(pao) return get_email_from_pao(pao)
def get_email_from_pao(pao): def get_email_from_pao(pao: ifcopenshell.entity_instance) -> Union[str, None]:
for address in pao.ThePerson.Addresses or []: for address in pao.ThePerson.Addresses or []:
if address.is_a("IfcTelecomAddress") and address.ElectronicMailAddresses: if address.is_a("IfcTelecomAddress") and address.ElectronicMailAddresses:
return address.ElectronicMailAddresses[0] return address.ElectronicMailAddresses[0]
@@ -986,23 +992,25 @@ def get_email_from_pao(pao):
return pao.ThePerson.GivenName + pao.ThePerson.FamilyName + "@" + pao.TheOrganization.Name + ".com" return pao.ThePerson.GivenName + pao.ThePerson.FamilyName + "@" + pao.TheOrganization.Name + ".com"
def get_owner_name(element): def get_owner_name(element: ifcopenshell.entity_instance) -> Union[str, None]:
if getattr(element, "OwnerHistory", None): if getattr(element, "OwnerHistory", None):
return element.OwnerHistory.OwningUser.TheOrganization.Name return element.OwnerHistory.OwningUser.TheOrganization.Name
def get_created_on(element): def get_created_on(element: ifcopenshell.entity_instance) -> str:
if getattr(element, "OwnerHistory", None): if getattr(element, "OwnerHistory", None):
return ifcopenshell.util.date.ifc2datetime(element.OwnerHistory.CreationDate).isoformat() return ifcopenshell.util.date.ifc2datetime(element.OwnerHistory.CreationDate).isoformat()
return "1900-12-31T23:59:59" # Yes, really return "1900-12-31T23:59:59" # Yes, really
def get_external_system(element): def get_external_system(element: ifcopenshell.entity_instance) -> Union[str, None]:
if getattr(element, "OwnerHistory", None): if getattr(element, "OwnerHistory", None):
return val(element.OwnerHistory.OwningApplication.ApplicationFullName) return val(element.OwnerHistory.OwningApplication.ApplicationFullName)
def get_facility_parent(element, ifc_class): def get_facility_parent(
element: ifcopenshell.entity_instance, ifc_class: str
) -> Union[ifcopenshell.entity_instance, None]:
parent = ifcopenshell.util.element.get_aggregate(element) parent = ifcopenshell.util.element.get_aggregate(element)
while parent: while parent:
if parent.is_a(ifc_class): if parent.is_a(ifc_class):
@@ -1012,11 +1020,11 @@ def get_facility_parent(element, ifc_class):
parent = ifcopenshell.util.element.get_aggregate(parent) parent = ifcopenshell.util.element.get_aggregate(parent)
def val(x): def val(x: Any) -> Any:
return x if x not in ("", "n/a") else None return x if x not in ("", "n/a") else None
def get_area_measurement(element): def get_area_measurement(element: ifcopenshell.entity_instance) -> Union[str, None]:
for relationship in getattr(element, "IsDefinedBy", []) or []: for relationship in getattr(element, "IsDefinedBy", []) or []:
if relationship.is_a("IfcRelDefinesByProperties"): if relationship.is_a("IfcRelDefinesByProperties"):
definition = relationship.RelatingPropertyDefinition definition = relationship.RelatingPropertyDefinition
@@ -1029,7 +1037,7 @@ def get_area_measurement(element):
return result return result
def get_category(element): def get_category(element: ifcopenshell.entity_instance) -> Union[str, None]:
references = list(ifcopenshell.util.classification.get_references(element)) references = list(ifcopenshell.util.classification.get_references(element))
results = [] results = []
for reference in references: for reference in references:
@@ -1078,7 +1086,7 @@ def get_category(element):
return val(getattr(element, "ObjectType", None)) return val(getattr(element, "ObjectType", None))
def get_pao_address(element, name): def get_pao_address(element: ifcopenshell.entity_instance, name: str) -> Union[str, None]:
for actor in [element.ThePerson, element.TheOrganization]: for actor in [element.ThePerson, element.TheOrganization]:
for address in actor.Addresses or []: for address in actor.Addresses or []:
if hasattr(address, name) and getattr(address, name, None): if hasattr(address, name) and getattr(address, name, None):
@@ -1090,7 +1098,9 @@ def get_pao_address(element, name):
return result return result
def get_property(psets, pset_name, prop_name, decimals=None): def get_property(
psets: dict[str, dict[str, Any]], pset_name: str, prop_name: str, decimals: Optional[int] = None
) -> Union[Any, None, float]:
if pset_name in psets: if pset_name in psets:
result = psets[pset_name].get(prop_name, None) result = psets[pset_name].get(prop_name, None)
if decimals is None or result is None: if decimals is None or result is None:
@@ -1098,13 +1108,13 @@ def get_property(psets, pset_name, prop_name, decimals=None):
return round(result, decimals) return round(result, decimals)
def get_history(ifc_file): def get_history(ifc_file: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None]:
histories = ifc_file.by_type("IfcOwnerHistory") histories = ifc_file.by_type("IfcOwnerHistory")
if histories: if histories:
return sorted(histories, key=lambda x: x.id())[-1] return sorted(histories, key=lambda x: x.id())[-1]
def get_property_unit(pset, prop_name): def get_property_unit(pset: ifcopenshell.entity_instance, prop_name: str) -> Union[str, None]:
for prop in getattr(pset, "HasProperties", []) or []: for prop in getattr(pset, "HasProperties", []) or []:
if prop.Name == prop_name: if prop.Name == prop_name:
unit = getattr(prop, "Unit", None) unit = getattr(prop, "Unit", None)
@@ -1112,7 +1122,8 @@ def get_property_unit(pset, prop_name):
return get_unit_name(unit) return get_unit_name(unit)
def get_allowed_values(pset_id, prop_name): # TODO: never used?
def get_allowed_values(ifc_file: ifcopenshell.file, pset_id: int, prop_name: str) -> Union[str, None]:
pset = ifc_file.by_id(pset_id) pset = ifc_file.by_id(pset_id)
for prop in getattr(pset, "HasProperties", []) or []: for prop in getattr(pset, "HasProperties", []) or []:
if prop.Name == prop_name: if prop.Name == prop_name:
@@ -1120,7 +1131,7 @@ def get_allowed_values(pset_id, prop_name):
return ",".join([v.wrappedValue for v in prop.EnumerationValues]) return ",".join([v.wrappedValue for v in prop.EnumerationValues])
def get_sheet_name(element): def get_sheet_name(element: ifcopenshell.entity_instance) -> Union[str, None]:
if element.is_a("IfcBuilding"): if element.is_a("IfcBuilding"):
return "Facility" return "Facility"
elif element.is_a("IfcBuildingStorey"): elif element.is_a("IfcBuildingStorey"):
@@ -19,6 +19,7 @@
import ifcopenshell import ifcopenshell
import ifcopenshell.guid import ifcopenshell.guid
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.representation
from typing import Any, Callable, Optional, Union, Literal, overload from typing import Any, Callable, Optional, Union, Literal, overload
from collections import namedtuple from collections import namedtuple
@@ -17,7 +17,10 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell import ifcopenshell
import ifcopenshell.ifcopenshell_wrapper as W
import ifcopenshell.util.attribute import ifcopenshell.util.attribute
from typing import Literal
from typing_extensions import assert_never
# COBie actually uses an exclusion list, but this inclusion list is equivalent. # COBie actually uses an exclusion list, but this inclusion list is equivalent.
cobie_type_classes = [ cobie_type_classes = [
@@ -114,7 +117,7 @@ fmhem_excluded_classes = [
] ]
def get_cobie_types(ifc_file): def get_cobie_types(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
elements = [] elements = []
for ifc_class in cobie_type_classes: for ifc_class in cobie_type_classes:
try: try:
@@ -124,7 +127,7 @@ def get_cobie_types(ifc_file):
return elements return elements
def get_cobie_components(ifc_file): def get_cobie_components(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
elements = [] elements = []
for ifc_class in cobie_component_classes: for ifc_class in cobie_component_classes:
try: try:
@@ -134,7 +137,7 @@ def get_cobie_components(ifc_file):
return elements return elements
def get_fmhem_types(ifc_file): def get_fmhem_types(ifc_file: ifcopenshell.file) -> list[ifcopenshell.entity_instance]:
elements = [] elements = []
if ifc_file.schema == "IFC2X3": if ifc_file.schema == "IFC2X3":
fmhem_classes = fmhem_classes_ifc2x3 fmhem_classes = fmhem_classes_ifc2x3
@@ -148,10 +151,10 @@ def get_fmhem_types(ifc_file):
return elements return elements
def get_fmhem_classes(schema="IFC4"): def get_fmhem_classes(schema: Literal["IFC4", "IFC2X3"] = "IFC4") -> dict[str, list[str]]:
results = {} results = {}
def get_fmhem_class(declaration): def get_fmhem_class(declaration: W.entity) -> None:
if declaration.name() in fmhem_excluded_classes: if declaration.name() in fmhem_excluded_classes:
pass pass
elif declaration.is_abstract(): elif declaration.is_abstract():
@@ -172,8 +175,10 @@ def get_fmhem_classes(schema="IFC4"):
classes = fmhem_classes_ifc4 classes = fmhem_classes_ifc4
elif schema == "IFC2X3": elif schema == "IFC2X3":
classes = fmhem_classes_ifc2x3 classes = fmhem_classes_ifc2x3
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema) else:
assert_never(schema)
schema_ = ifcopenshell.schema_by_name(schema)
for ifc_class in classes: for ifc_class in classes:
declaration = schema.declaration_by_name(ifc_class) declaration = schema_.declaration_by_name(ifc_class)
get_fmhem_class(declaration) get_fmhem_class(declaration)
return results return results