diff --git a/src/bonsai/bonsai/bim/module/pset_template/data.py b/src/bonsai/bonsai/bim/module/pset_template/data.py index 76ceaa18fa..eb2a52c699 100644 --- a/src/bonsai/bonsai/bim/module/pset_template/data.py +++ b/src/bonsai/bonsai/bim/module/pset_template/data.py @@ -24,6 +24,7 @@ import ifcopenshell.util.attribute import ifcopenshell.util.doc import bonsai.tool as tool from bonsai.bim.ifc import IfcStore +from typing import Any def refresh(): @@ -49,7 +50,7 @@ class PsetTemplatesData: cls.data["prop_templates"] = cls.prop_templates() @classmethod - def primary_measure_type(cls): + def primary_measure_type(cls) -> list[tuple[str, str, str]]: ifc_file = IfcStore.pset_template_file if not ifc_file: return [] @@ -61,7 +62,7 @@ class PsetTemplatesData: ] @classmethod - def property_template_type(cls): + def property_template_type(cls) -> list[tuple[str, str, str]]: ifc_file = IfcStore.pset_template_file if not ifc_file: return [] @@ -96,7 +97,7 @@ class PsetTemplatesData: return [(str(t.id()), t.Name, "") for t in IfcStore.pset_template_file.by_type("IfcPropertySetTemplate")] @classmethod - def pset_template(cls): + def pset_template(cls) -> dict[str, Any]: props = bpy.context.scene.BIMPsetTemplateProperties template_id = props.pset_templates if not template_id: @@ -109,7 +110,7 @@ class PsetTemplatesData: return info @classmethod - def prop_templates(cls): + def prop_templates(cls) -> list[dict[str, Any]]: props = bpy.context.scene.BIMPsetTemplateProperties template_id = props.pset_templates if not template_id: diff --git a/src/ifc4d/ifc4d/csv2ifc.py b/src/ifc4d/ifc4d/csv2ifc.py index fca4e98a91..da40fe980a 100644 --- a/src/ifc4d/ifc4d/csv2ifc.py +++ b/src/ifc4d/ifc4d/csv2ifc.py @@ -22,7 +22,19 @@ import ifcopenshell.api import ifcopenshell.util.date import ifcopenshell.util.unit import datetime -from typing import Any, Optional, Union +from typing import Any, Optional, Union, Literal, get_args + + +SUPPORTED_COLUMN = Literal[ + "HIERARCHY", + "TYPE", # CREW, LABOR, EQUIPMENT, SUBCONTRACTOR, MATERIAL, PRODUCT. + "ACTIVITY/RESOURCE NAME", + "DESCRIPTION", + "COST", + "QUANTITY NAME", + "LABOR OUTPUT", + "EQUIPMENT OUTPUT", +] class Csv2Ifc: @@ -32,7 +44,7 @@ class Csv2Ifc: Notes about format: - empty rows are skipped. - 'HIERARCHY' must be the first column. - - See SUPPORTED_COLUMNS below for the list of supported columns. + - See SUPPORTED_COLUMN above for the list of supported columns. Example: @@ -42,17 +54,6 @@ class Csv2Ifc: csv2ifc.execute() """ - SUPPORTED_COLUMNS = ( - "HIERARCHY", - "TYPE", # CREW, LABOR, EQUIPMENT, SUBCONTRACTOR, MATERIAL, PRODUCT. - "ACTIVITY/RESOURCE NAME", - "DESCRIPTION", - "COST", - "QUANTITY NAME", - "LABOR OUTPUT", - "EQUIPMENT OUTPUT", - ) - def __init__(self, csv: str, ifc_file: Optional[ifcopenshell.file] = None): """ :param csv: CSV filepath to load resources from. @@ -78,7 +79,7 @@ class Csv2Ifc: def parse_csv(self) -> None: self.parents = {} - self.headers: dict[str, int] = {} + self.headers: dict[SUPPORTED_COLUMN, int] = {} with open(self.csv, "r") as csv_file: reader = csv.reader(csv_file) for row in reader: @@ -86,7 +87,7 @@ class Csv2Ifc: if not row[0]: continue if row[0] == "HIERARCHY": - missing_columns = set(self.SUPPORTED_COLUMNS) - set(row) + missing_columns = set(get_args(SUPPORTED_COLUMN)) - set(row) if missing_columns: raise Exception( f"Header is missing some of the required columns: {', '.join(missing_columns)}." diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py index ef17b78153..15816cd625 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py @@ -17,15 +17,16 @@ # along with IfcOpenShell. If not, see . import ifcopenshell +import ifcopenshell.api.pset import ifcopenshell.util.pset -from typing import Optional, Any +from typing import Optional, Any, Union def edit_qto( file: ifcopenshell.file, qto: ifcopenshell.entity_instance, name: Optional[str] = None, - properties: Optional[dict[str, Any]] = None, + properties: Optional[dict[str, Union[ifcopenshell.entity_instance, float, int]]] = None, pset_template: Optional[ifcopenshell.entity_instance] = None, ) -> None: """Edits a quantity set and its quantities @@ -40,10 +41,8 @@ def edit_qto( It is not allowed to have None quantities in IFC. :param qto: The IfcElementQuantity to edit. - :type qto: ifcopenshell.entity_instance :param name: A new name for the quantity set. If no name is specified, the quantity set name is not changed. - :type name: str, optional :param properties: A dictionary of properties. The keys must be a string of the name of the quantity. The data type of the value will be determined by the quantity set template. If no quantity set @@ -53,13 +52,11 @@ def edit_qto( become IfcBoolean, and integers will become IfcInteger. If more control is desired, you may explicitly specify IFC data objects directly. - :type properties: dict :param pset_template: If a quantity set template is provided, this will be used to determine data types. If no user-defined template is provided, the built-in buildingSMART templates will be loaded. :type pset_template: ifcopenshell.entity_instance, optional :return: None - :rtype: None Example: @@ -121,6 +118,9 @@ def edit_qto( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + def execute(self): self.qto_idx = 5 if self.settings["qto"].is_a("IfcPhysicalComplexQuantity"): @@ -132,22 +132,22 @@ class Usecase: new_properties = self.add_new_properties() self.extend_qto_with_new_properties(new_properties) - def update_qto_name(self): + def update_qto_name(self) -> None: if self.settings["name"]: self.settings["qto"].Name = self.settings["name"] - def load_qto_template(self): + def load_qto_template(self) -> None: if self.settings["pset_template"]: self.pset_template = self.settings["pset_template"] else: self.psetqto = ifcopenshell.util.pset.get_template(self.file.schema_identifier) self.qto_template = self.psetqto.get_by_name(self.settings["qto"].Name) - def update_existing_properties(self): + def update_existing_properties(self) -> None: for prop in self.settings["qto"][self.qto_idx] or []: self.update_existing_property(prop) - def update_existing_property(self, prop): + def update_existing_property(self, prop: ifcopenshell.entity_instance) -> None: if prop.Name not in self.settings["properties"]: return value = self.settings["properties"][prop.Name] @@ -162,7 +162,7 @@ class Usecase: prop[3] = float(value) del self.settings["properties"][name] - def add_new_properties(self): + def add_new_properties(self) -> list[ifcopenshell.entity_instance]: properties = [] for name, value in self.settings["properties"].items(): if value is None: @@ -184,12 +184,12 @@ class Usecase: ) return properties - def extend_qto_with_new_properties(self, new_properties): + def extend_qto_with_new_properties(self, new_properties: list[ifcopenshell.entity_instance]) -> None: props = list(self.settings["qto"][self.qto_idx]) if self.settings["qto"][self.qto_idx] else [] props.extend(new_properties) self.settings["qto"][self.qto_idx] = props - def get_canonical_property_type(self, name, value): + def get_canonical_property_type(self, name: str, value: Union[ifcopenshell.entity_instance, float, int]) -> str: if isinstance(value, ifcopenshell.entity_instance): result = value.is_a().replace("Ifc", "").replace("Measure", "") # Sigh, IFC inconsistencies @@ -205,7 +205,8 @@ class Usecase: return prop_template.TemplateType[2:].lower().capitalize() return "Length" - def get_primary_measure_type(self, name, previous_value=None): + # TODO: unused code. + def get_primary_measure_type(self, name: str, previous_value: Optional[Any] = None) -> str: if not self.qto_template: return previous_value.is_a() if previous_value else "IfcLabel" for prop_template in self.qto_template.HasPropertyTemplates: diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index 94a44c246a..353634d81c 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -271,7 +271,7 @@ def get_quantity( if quantity[0] != name: continue if quantity.is_a("IfcPhysicalSimpleQuantity"): - # 3 IfcPhysicalSimpleQuantity.Unit + # 3 IfcPhysicalSimpleQuantity.XXXValue result = quantity[3] elif quantity.is_a("IfcPhysicalComplexQuantity"): data = {k: v for k, v in quantity.get_info().items() if v is not None and k != "Name"} @@ -299,7 +299,7 @@ def get_quantities( # 0 IfcPhysicalQuantity.Name quantity_name = quantity[0] if quantity.is_a("IfcPhysicalSimpleQuantity"): - # 3 IfcPhysicalSimpleQuantity.Unit + # 3 IfcPhysicalSimpleQuantity.XXXValue results[quantity_name] = quantity[3] if verbose: results[quantity_name] = {