This commit is contained in:
Andrej730
2024-11-15 16:41:02 +05:00
parent 7f40491e41
commit dd0d0ed1af
4 changed files with 38 additions and 35 deletions
@@ -24,6 +24,7 @@ import ifcopenshell.util.attribute
import ifcopenshell.util.doc import ifcopenshell.util.doc
import bonsai.tool as tool import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
from typing import Any
def refresh(): def refresh():
@@ -49,7 +50,7 @@ class PsetTemplatesData:
cls.data["prop_templates"] = cls.prop_templates() cls.data["prop_templates"] = cls.prop_templates()
@classmethod @classmethod
def primary_measure_type(cls): def primary_measure_type(cls) -> list[tuple[str, str, str]]:
ifc_file = IfcStore.pset_template_file ifc_file = IfcStore.pset_template_file
if not ifc_file: if not ifc_file:
return [] return []
@@ -61,7 +62,7 @@ class PsetTemplatesData:
] ]
@classmethod @classmethod
def property_template_type(cls): def property_template_type(cls) -> list[tuple[str, str, str]]:
ifc_file = IfcStore.pset_template_file ifc_file = IfcStore.pset_template_file
if not ifc_file: if not ifc_file:
return [] return []
@@ -96,7 +97,7 @@ class PsetTemplatesData:
return [(str(t.id()), t.Name, "") for t in IfcStore.pset_template_file.by_type("IfcPropertySetTemplate")] return [(str(t.id()), t.Name, "") for t in IfcStore.pset_template_file.by_type("IfcPropertySetTemplate")]
@classmethod @classmethod
def pset_template(cls): def pset_template(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMPsetTemplateProperties props = bpy.context.scene.BIMPsetTemplateProperties
template_id = props.pset_templates template_id = props.pset_templates
if not template_id: if not template_id:
@@ -109,7 +110,7 @@ class PsetTemplatesData:
return info return info
@classmethod @classmethod
def prop_templates(cls): def prop_templates(cls) -> list[dict[str, Any]]:
props = bpy.context.scene.BIMPsetTemplateProperties props = bpy.context.scene.BIMPsetTemplateProperties
template_id = props.pset_templates template_id = props.pset_templates
if not template_id: if not template_id:
+16 -15
View File
@@ -22,7 +22,19 @@ import ifcopenshell.api
import ifcopenshell.util.date import ifcopenshell.util.date
import ifcopenshell.util.unit import ifcopenshell.util.unit
import datetime 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: class Csv2Ifc:
@@ -32,7 +44,7 @@ class Csv2Ifc:
Notes about format: Notes about format:
- empty rows are skipped. - empty rows are skipped.
- 'HIERARCHY' must be the first column. - '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: Example:
@@ -42,17 +54,6 @@ class Csv2Ifc:
csv2ifc.execute() 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): def __init__(self, csv: str, ifc_file: Optional[ifcopenshell.file] = None):
""" """
:param csv: CSV filepath to load resources from. :param csv: CSV filepath to load resources from.
@@ -78,7 +79,7 @@ class Csv2Ifc:
def parse_csv(self) -> None: def parse_csv(self) -> None:
self.parents = {} self.parents = {}
self.headers: dict[str, int] = {} self.headers: dict[SUPPORTED_COLUMN, int] = {}
with open(self.csv, "r") as csv_file: with open(self.csv, "r") as csv_file:
reader = csv.reader(csv_file) reader = csv.reader(csv_file)
for row in reader: for row in reader:
@@ -86,7 +87,7 @@ class Csv2Ifc:
if not row[0]: if not row[0]:
continue continue
if row[0] == "HIERARCHY": if row[0] == "HIERARCHY":
missing_columns = set(self.SUPPORTED_COLUMNS) - set(row) missing_columns = set(get_args(SUPPORTED_COLUMN)) - set(row)
if missing_columns: if missing_columns:
raise Exception( raise Exception(
f"Header is missing some of the required columns: {', '.join(missing_columns)}." f"Header is missing some of the required columns: {', '.join(missing_columns)}."
@@ -17,15 +17,16 @@
# 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.api.pset
import ifcopenshell.util.pset import ifcopenshell.util.pset
from typing import Optional, Any from typing import Optional, Any, Union
def edit_qto( def edit_qto(
file: ifcopenshell.file, file: ifcopenshell.file,
qto: ifcopenshell.entity_instance, qto: ifcopenshell.entity_instance,
name: Optional[str] = None, 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, pset_template: Optional[ifcopenshell.entity_instance] = None,
) -> None: ) -> None:
"""Edits a quantity set and its quantities """Edits a quantity set and its quantities
@@ -40,10 +41,8 @@ def edit_qto(
It is not allowed to have None quantities in IFC. It is not allowed to have None quantities in IFC.
:param qto: The IfcElementQuantity to edit. :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, :param name: A new name for the quantity set. If no name is specified,
the quantity set name is not changed. the quantity set name is not changed.
:type name: str, optional
:param properties: A dictionary of properties. The keys must be a string :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 of the name of the quantity. The data type of the value will be
determined by the quantity set template. If no quantity set 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 become IfcBoolean, and integers will become IfcInteger. If more
control is desired, you may explicitly specify IFC data objects control is desired, you may explicitly specify IFC data objects
directly. directly.
:type properties: dict
:param pset_template: If a quantity set template is provided, this will :param pset_template: If a quantity set template is provided, this will
be used to determine data types. If no user-defined template is be used to determine data types. If no user-defined template is
provided, the built-in buildingSMART templates will be loaded. provided, the built-in buildingSMART templates will be loaded.
:type pset_template: ifcopenshell.entity_instance, optional :type pset_template: ifcopenshell.entity_instance, optional
:return: None :return: None
:rtype: None
Example: Example:
@@ -121,6 +118,9 @@ def edit_qto(
class Usecase: class Usecase:
file: ifcopenshell.file
settings: dict[str, Any]
def execute(self): def execute(self):
self.qto_idx = 5 self.qto_idx = 5
if self.settings["qto"].is_a("IfcPhysicalComplexQuantity"): if self.settings["qto"].is_a("IfcPhysicalComplexQuantity"):
@@ -132,22 +132,22 @@ class Usecase:
new_properties = self.add_new_properties() new_properties = self.add_new_properties()
self.extend_qto_with_new_properties(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"]: if self.settings["name"]:
self.settings["qto"].Name = 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"]: if self.settings["pset_template"]:
self.pset_template = self.settings["pset_template"] self.pset_template = self.settings["pset_template"]
else: else:
self.psetqto = ifcopenshell.util.pset.get_template(self.file.schema_identifier) self.psetqto = ifcopenshell.util.pset.get_template(self.file.schema_identifier)
self.qto_template = self.psetqto.get_by_name(self.settings["qto"].Name) 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 []: for prop in self.settings["qto"][self.qto_idx] or []:
self.update_existing_property(prop) 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"]: if prop.Name not in self.settings["properties"]:
return return
value = self.settings["properties"][prop.Name] value = self.settings["properties"][prop.Name]
@@ -162,7 +162,7 @@ class Usecase:
prop[3] = float(value) prop[3] = float(value)
del self.settings["properties"][name] del self.settings["properties"][name]
def add_new_properties(self): def add_new_properties(self) -> list[ifcopenshell.entity_instance]:
properties = [] properties = []
for name, value in self.settings["properties"].items(): for name, value in self.settings["properties"].items():
if value is None: if value is None:
@@ -184,12 +184,12 @@ class Usecase:
) )
return properties 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 = list(self.settings["qto"][self.qto_idx]) if self.settings["qto"][self.qto_idx] else []
props.extend(new_properties) props.extend(new_properties)
self.settings["qto"][self.qto_idx] = props 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): if isinstance(value, ifcopenshell.entity_instance):
result = value.is_a().replace("Ifc", "").replace("Measure", "") result = value.is_a().replace("Ifc", "").replace("Measure", "")
# Sigh, IFC inconsistencies # Sigh, IFC inconsistencies
@@ -205,7 +205,8 @@ class Usecase:
return prop_template.TemplateType[2:].lower().capitalize() return prop_template.TemplateType[2:].lower().capitalize()
return "Length" 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: if not self.qto_template:
return previous_value.is_a() if previous_value else "IfcLabel" return previous_value.is_a() if previous_value else "IfcLabel"
for prop_template in self.qto_template.HasPropertyTemplates: for prop_template in self.qto_template.HasPropertyTemplates:
@@ -271,7 +271,7 @@ def get_quantity(
if quantity[0] != name: if quantity[0] != name:
continue continue
if quantity.is_a("IfcPhysicalSimpleQuantity"): if quantity.is_a("IfcPhysicalSimpleQuantity"):
# 3 IfcPhysicalSimpleQuantity.Unit # 3 IfcPhysicalSimpleQuantity.XXXValue
result = quantity[3] result = quantity[3]
elif quantity.is_a("IfcPhysicalComplexQuantity"): elif quantity.is_a("IfcPhysicalComplexQuantity"):
data = {k: v for k, v in quantity.get_info().items() if v is not None and k != "Name"} 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 # 0 IfcPhysicalQuantity.Name
quantity_name = quantity[0] quantity_name = quantity[0]
if quantity.is_a("IfcPhysicalSimpleQuantity"): if quantity.is_a("IfcPhysicalSimpleQuantity"):
# 3 IfcPhysicalSimpleQuantity.Unit # 3 IfcPhysicalSimpleQuantity.XXXValue
results[quantity_name] = quantity[3] results[quantity_name] = quantity[3]
if verbose: if verbose:
results[quantity_name] = { results[quantity_name] = {