mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Merge remote-tracking branch 'origin/v0.6.0' into v0.7.0
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"cost_item": None, "products": []}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
quantity_names = set()
|
||||
for quantity in self.settings["cost_item"].CostQuantities or []:
|
||||
if quantity.Name:
|
||||
quantity_names.add(quantity.Name)
|
||||
|
||||
for product in self.settings["products"]:
|
||||
ifcopenshell.api.run(
|
||||
"control.assign_control",
|
||||
self.file,
|
||||
related_object=product,
|
||||
relating_control=self.settings["cost_item"],
|
||||
)
|
||||
|
||||
for name in quantity_names:
|
||||
ifcopenshell.api.run(
|
||||
"cost.assign_cost_item_product_quantities",
|
||||
self.file,
|
||||
cost_item=self.settings["cost_item"],
|
||||
prop_name=name
|
||||
)
|
||||
+14
-11
@@ -4,25 +4,28 @@ import ifcopenshell.api
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"cost_item": None, "prop_name": ""}
|
||||
self.settings = {"cost_item": None, "products": [], "prop_name": ""}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
|
||||
for control in self.settings["cost_item"].Controls or []:
|
||||
for related_object in control.RelatedObjects:
|
||||
self.add_quantity_from_related_object(related_object)
|
||||
for product in self.settings["products"]:
|
||||
ifcopenshell.api.run(
|
||||
"control.assign_control",
|
||||
self.file,
|
||||
related_object=product,
|
||||
relating_control=self.settings["cost_item"],
|
||||
)
|
||||
self.add_quantity_from_related_object(product)
|
||||
self.settings["cost_item"].CostQuantities = list(self.quantities)
|
||||
|
||||
def add_quantity_from_related_object(self, element):
|
||||
if element.is_a("IfcTypeObject"):
|
||||
for definition in element.HasPropertySets or []:
|
||||
self.add_quantity_from_qto(definition)
|
||||
else:
|
||||
for relationship in element.IsDefinedBy:
|
||||
if relationship.is_a("IfcRelDefinesByProperties"):
|
||||
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
|
||||
if not element.is_a("IfcObject"):
|
||||
return
|
||||
for relationship in element.IsDefinedBy:
|
||||
if relationship.is_a("IfcRelDefinesByProperties"):
|
||||
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
|
||||
|
||||
def add_quantity_from_qto(self, qto):
|
||||
if not qto.is_a("IfcElementQuantity"):
|
||||
@@ -51,21 +51,43 @@ class Data:
|
||||
del data["OwnerHistory"]
|
||||
del data["CostValues"]
|
||||
data["IsNestedBy"] = []
|
||||
data["Controls"] = []
|
||||
data["Controls"] = {}
|
||||
for rel in cost_item.IsNestedBy:
|
||||
[data["IsNestedBy"].append(o.id()) for o in rel.RelatedObjects if o.is_a("IfcCostItem")]
|
||||
parametric_quantities = []
|
||||
for rel in cost_item.Controls:
|
||||
[data["Controls"].append(o.id()) for o in rel.RelatedObjects or []]
|
||||
for related_object in rel.RelatedObjects or []:
|
||||
quantities = cls.get_object_quantities(cost_item, related_object)
|
||||
data["Controls"][related_object.id()] = quantities
|
||||
parametric_quantities.extend(quantities)
|
||||
cls.cost_items[cost_item.id()] = data
|
||||
cls.load_cost_item_quantities(cost_item, data)
|
||||
cls.load_cost_item_quantities(cost_item, data, parametric_quantities)
|
||||
cls.load_cost_item_values(cost_item, data)
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def load_cost_item_quantities(cls, cost_item, data):
|
||||
def get_object_quantities(cls, cost_item, element):
|
||||
if not element.is_a("IfcObject"):
|
||||
return []
|
||||
results = []
|
||||
for relationship in element.IsDefinedBy:
|
||||
if not relationship.is_a("IfcRelDefinesByProperties"):
|
||||
continue
|
||||
qto = relationship.RelatingPropertyDefinition
|
||||
if not qto.is_a("IfcElementQuantity"):
|
||||
continue
|
||||
for prop in qto.Quantities:
|
||||
if prop in cost_item.CostQuantities or []:
|
||||
results.append(prop.id())
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def load_cost_item_quantities(cls, cost_item, data, parametric_quantities):
|
||||
data["CostQuantities"] = []
|
||||
data["TotalCostQuantity"] = cls.get_total_quantity(cost_item)
|
||||
for quantity in cost_item.CostQuantities or []:
|
||||
if quantity.id() in parametric_quantities:
|
||||
continue
|
||||
quantity_data = quantity.get_info()
|
||||
del quantity_data["Unit"]
|
||||
cls.physical_quantities[quantity.id()] = quantity_data
|
||||
@@ -98,7 +120,11 @@ class Data:
|
||||
def load_cost_item_value(cls, cost_item_data, cost_item, cost_value):
|
||||
value_data = cost_value.get_info()
|
||||
del value_data["AppliedValue"]
|
||||
del value_data["UnitBasis"]
|
||||
if value_data["UnitBasis"]:
|
||||
data = cost_value.UnitBasis.get_info()
|
||||
data["ValueComponent"] = data["ValueComponent"].wrappedValue
|
||||
data["UnitComponent"] = data["UnitComponent"].id()
|
||||
value_data["UnitBasis"] = data
|
||||
if value_data["ApplicableDate"]:
|
||||
value_data["ApplicableDate"] = ifcopenshell.util.date.ifc2datetime(value_data["ApplicableDate"])
|
||||
if value_data["FixedUntilDate"]:
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
@@ -10,4 +15,19 @@ class Usecase:
|
||||
if name == "AppliedValue" and value is not None:
|
||||
# TODO: support all applied value select types
|
||||
value = self.file.createIfcMonetaryMeasure(value)
|
||||
elif name == "UnitBasis":
|
||||
self.remove_existing_unit_basis()
|
||||
if value:
|
||||
value_component = self.file.create_entity(
|
||||
ifcopenshell.util.unit.get_unit_measure_type(value["UnitComponent"].UnitType),
|
||||
value["ValueComponent"],
|
||||
)
|
||||
value = self.file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"])
|
||||
setattr(self.settings["cost_value"], name, value)
|
||||
|
||||
def remove_existing_unit_basis(self):
|
||||
if (
|
||||
self.settings["cost_value"].UnitBasis
|
||||
and len(self.file.get_inverse(self.settings["cost_value"].UnitBasis)) == 1
|
||||
):
|
||||
ifcopenshell.util.element.remove_deep(self.file, self.settings["cost_value"].UnitBasis)
|
||||
|
||||
@@ -9,7 +9,7 @@ class Usecase:
|
||||
layers = list(self.settings["layer_set"].MaterialLayers or [])
|
||||
layer = self.file.create_entity("IfcMaterialLayer", **{
|
||||
"Material": self.settings["material"],
|
||||
"LayerThickness": 0.
|
||||
"LayerThickness": 1.
|
||||
})
|
||||
layers.append(layer)
|
||||
self.settings["layer_set"].MaterialLayers = layers
|
||||
|
||||
@@ -11,6 +11,7 @@ class Usecase():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
# TODO: don't also edit the profile def in this usecase
|
||||
for name, value in self.settings["attributes"].items():
|
||||
setattr(self.settings["profile"], name, value)
|
||||
self.settings["profile"].Material = self.settings["material"]
|
||||
|
||||
@@ -7,3 +7,4 @@ class Usecase:
|
||||
|
||||
def execute(self):
|
||||
self.file.remove(self.settings["profile"])
|
||||
# TODO: deep purge
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class Usecase():
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"profile": None, "attributes": {}}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for name, value in self.settings["attributes"].items():
|
||||
setattr(self.settings["profile"], name, value)
|
||||
@@ -0,0 +1,10 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"profile": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.file.remove(self.settings["profile"])
|
||||
# TODO: deep purge
|
||||
@@ -11,12 +11,15 @@ class Usecase:
|
||||
|
||||
def execute(self):
|
||||
self.added_elements = set()
|
||||
self.whitelisted_inverse_attributes = {}
|
||||
if self.settings["element"].is_a("IfcTypeProduct"):
|
||||
return self.append_type_product()
|
||||
elif self.settings["element"].is_a("IfcMaterial"):
|
||||
return self.append_material()
|
||||
elif self.settings["element"].is_a("IfcCostSchedule"):
|
||||
return self.append_cost_schedule()
|
||||
elif self.settings["element"].is_a("IfcProfileDef"):
|
||||
return self.append_profile_def()
|
||||
|
||||
def is_already_appended(self):
|
||||
try:
|
||||
@@ -36,6 +39,12 @@ class Usecase:
|
||||
self.whitelisted_inverse_attributes = {"IfcCostSchedule": ["Controls"], "IfcCostItem": ["IsNestedBy"]}
|
||||
return self.add_element(self.settings["element"])
|
||||
|
||||
def append_profile_def(self):
|
||||
if [e for e in self.file.by_type("IfcProfileDef") if e.ProfileName == self.settings["element"].ProfileName]:
|
||||
return
|
||||
self.whitelisted_inverse_attributes = {"IfcProfileDef": ["HasProperties"]}
|
||||
return self.add_element(self.settings["element"])
|
||||
|
||||
def append_type_product(self):
|
||||
if self.is_already_appended():
|
||||
return
|
||||
|
||||
@@ -64,3 +64,15 @@ class Usecase:
|
||||
"Material": self.settings["product"],
|
||||
}
|
||||
)
|
||||
elif self.settings["product"].is_a("IfcProfileDef"):
|
||||
for definition in self.settings["product"].HasProperties or []:
|
||||
if definition.Name == self.settings["name"]:
|
||||
return definition
|
||||
|
||||
return self.file.create_entity(
|
||||
"IfcProfileProperties",
|
||||
**{
|
||||
"Name": self.settings["name"],
|
||||
"ProfileDefinition": self.settings["product"],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -26,6 +26,8 @@ class Data:
|
||||
cls.add_type_product_psets(product, product_id)
|
||||
elif product.is_a("IfcMaterialDefinition"):
|
||||
cls.add_material_psets(product, product_id)
|
||||
elif product.is_a("IfcProfileDef"):
|
||||
cls.add_profile_psets(product, product_id)
|
||||
else:
|
||||
cls.add_product_psets(product, product_id)
|
||||
|
||||
@@ -44,6 +46,13 @@ class Data:
|
||||
for pset in product.HasProperties:
|
||||
cls.add_pset(pset, product_id)
|
||||
|
||||
@classmethod
|
||||
def add_profile_psets(cls, product, product_id):
|
||||
if not product.HasProperties:
|
||||
return
|
||||
for pset in product.HasProperties:
|
||||
cls.add_pset(pset, product_id)
|
||||
|
||||
@classmethod
|
||||
def add_product_psets(cls, product, product_id):
|
||||
if not hasattr(product, "IsDefinedBy") or not product.IsDefinedBy:
|
||||
@@ -59,7 +68,7 @@ class Data:
|
||||
@classmethod
|
||||
def add_pset(cls, pset, product_id):
|
||||
data = pset.get_info()
|
||||
if not pset.is_a("IfcMaterialProperties"):
|
||||
if not pset.is_a("IfcMaterialProperties") and not pset.is_a("IfcProfileProperties"):
|
||||
del data["OwnerHistory"]
|
||||
del data["HasProperties"]
|
||||
if hasattr(pset, "HasProperties"):
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"unit_type": "LENGTHUNIT", "name": "METRE"}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
return self.file.create_entity("IfcSIUnit", UnitType=self.settings["unit_type"], Name=self.settings["name"])
|
||||
@@ -24,6 +24,9 @@ class Data:
|
||||
return
|
||||
for unit in unit_assignment[0].Units:
|
||||
cls.unit_assignment.append(unit.id())
|
||||
for unit in (
|
||||
cls.file.by_type("IfcDerivedUnit") + cls.file.by_type("IfcNamedUnit") + cls.file.by_type("IfcMonetaryUnit")
|
||||
):
|
||||
cls.load_unit(unit)
|
||||
cls.is_loaded = True
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"units": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
unit_assignment = self.file.by_type("IfcUnitAssignment")
|
||||
if not unit_assignment:
|
||||
return
|
||||
unit_assignment = unit_assignment[0]
|
||||
units = set(unit_assignment.Units or [])
|
||||
units = units - set(self.settings["units"])
|
||||
if units:
|
||||
unit_assignment.Units = list(units)
|
||||
return unit_assignment
|
||||
@@ -359,19 +359,27 @@ class file(object):
|
||||
return [entity_instance(e, self) for e in self.wrapped_data.by_type(type)]
|
||||
return [entity_instance(e, self) for e in self.wrapped_data.by_type_excl_subtypes(type)]
|
||||
|
||||
def traverse(self, inst, max_levels=None):
|
||||
def traverse(self, inst, max_levels=None, breadth_first=False):
|
||||
"""Get a list of all referenced instances for a particular instance including itself
|
||||
|
||||
:param inst: The entity instance to get all sub instances
|
||||
:type inst: ifcopenshell.entity_instance.entity_instance
|
||||
:param max_levels: How far deep to recursively fetch sub instances. None or -1 means infinite.
|
||||
:type max_levels: None|int
|
||||
:param breadth_first: Whether to use breadth-first search, the default is depth-first.
|
||||
:type max_levels: bool
|
||||
:returns: A list of ifcopenshell.entity_instance.entity_instance objects
|
||||
:rtype: list
|
||||
"""
|
||||
if max_levels is None:
|
||||
max_levels = -1
|
||||
return [entity_instance(e, self) for e in self.wrapped_data.traverse(inst.wrapped_data, max_levels)]
|
||||
|
||||
if breadth_first:
|
||||
fn = self.wrapped_data.traverse_breadth_first
|
||||
else:
|
||||
fn = self.wrapped_data.traverse
|
||||
|
||||
return [entity_instance(e, self) for e in fn(inst.wrapped_data, max_levels)]
|
||||
|
||||
def get_inverse(self, inst):
|
||||
"""Return a list of entities that reference this entity
|
||||
|
||||
@@ -104,7 +104,7 @@ def has_element_reference(value, element):
|
||||
def remove_deep(ifc_file, element):
|
||||
# @todo maybe some sort of try-finally mechanism.
|
||||
ifc_file.batch()
|
||||
subgraph = list(ifc_file.traverse(element))
|
||||
subgraph = list(ifc_file.traverse(element, breadth_first=True))
|
||||
subgraph_set = set(subgraph)
|
||||
for ref in subgraph[::-1]:
|
||||
if ref.id() and len(set(ifc_file.get_inverse(ref)) - subgraph_set) == 0:
|
||||
|
||||
@@ -25,7 +25,7 @@ unit_names = [
|
||||
"CANDELA",
|
||||
"COULOMB",
|
||||
"CUBIC_METRE",
|
||||
"DEGREE CELSIUS",
|
||||
"DEGREE_CELSIUS",
|
||||
"FARAD",
|
||||
"GRAM",
|
||||
"GRAY",
|
||||
@@ -43,7 +43,7 @@ unit_names = [
|
||||
"SECOND",
|
||||
"SIEMENS",
|
||||
"SIEVERT",
|
||||
"SQUARE METRE",
|
||||
"SQUARE_METRE",
|
||||
"METRE",
|
||||
"STERADIAN",
|
||||
"TESLA",
|
||||
@@ -52,7 +52,6 @@ unit_names = [
|
||||
"WEBER",
|
||||
]
|
||||
|
||||
|
||||
si_dimensions = {
|
||||
"METRE": (1, 0, 0, 0, 0, 0, 0),
|
||||
"SQUARE_METRE": (2, 0, 0, 0, 0, 0, 0),
|
||||
@@ -87,6 +86,72 @@ si_dimensions = {
|
||||
"OTHERWISE": (0, 0, 0, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
# See https://github.com/buildingSMART/IFC4.3.x-development/issues/72
|
||||
si_type_names = {
|
||||
"ABSORBEDDOSEUNIT": "GRAY",
|
||||
"AMOUNTOFSUBSTANCEUNIT": "MOLE",
|
||||
"AREAUNIT": "SQUARE_METRE",
|
||||
"DOSEEQUIVALENTUNIT": "SIEVERT",
|
||||
"ELECTRICCAPACITANCEUNIT": "FARAD",
|
||||
"ELECTRICCHARGEUNIT": "COULOMB",
|
||||
"ELECTRICCONDUCTANCEUNIT": "SIEMENS",
|
||||
"ELECTRICCURRENTUNIT": "AMPERE",
|
||||
"ELECTRICRESISTANCEUNIT": "OHM",
|
||||
"ELECTRICVOLTAGEUNIT": "VOLT",
|
||||
"ENERGYUNIT": "JOULE",
|
||||
"FORCEUNIT": "NEWTON",
|
||||
"FREQUENCYUNIT": "HERTZ",
|
||||
"ILLUMINANCEUNIT": "LUX",
|
||||
"INDUCTANCEUNIT": "HENRY",
|
||||
"LENGTHUNIT": "METRE",
|
||||
"LUMINOUSFLUXUNIT": "LUMEN",
|
||||
"LUMINOUSINTENSITYUNIT": "CANDELA",
|
||||
"MAGNETICFLUXDENSITYUNIT": "TESLA",
|
||||
"MAGNETICFLUXUNIT": "WEBER",
|
||||
"MASSUNIT": "GRAM",
|
||||
"PLANEANGLEUNIT": "RADIAN",
|
||||
"POWERUNIT": "WATT",
|
||||
"PRESSUREUNIT": "PASCAL",
|
||||
"RADIOACTIVITYUNIT": "BECQUEREL",
|
||||
"SOLIDANGLEUNIT": "STERADIAN",
|
||||
"THERMODYNAMICTEMPERATUREUNIT": "KELVIN", # Or, DEGREE_CELSIUS, but this is a quirk of IFC
|
||||
"TIMEUNIT": "SECOND",
|
||||
"VOLUMEUNIT": "CUBIC_METRE",
|
||||
}
|
||||
|
||||
# Are you good at physics? Want to help fill these in? :)
|
||||
named_dimensions = {
|
||||
# "ABSORBEDDOSEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
"AMOUNTOFSUBSTANCEUNIT": (0, 0, 0, 0, 0, 1, 0),
|
||||
"AREAUNIT": (2, 0, 0, 0, 0, 0, 0),
|
||||
# "DOSEEQUIVALENTUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
# "ELECTRICCAPACITANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
# "ELECTRICCHARGEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
# "ELECTRICCONDUCTANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
"ELECTRICCURRENTUNIT": (0, 0, 0, 1, 0, 0, 0),
|
||||
# "ELECTRICRESISTANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
# "ELECTRICVOLTAGEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
"ENERGYUNIT": (2, 1, -2, 0, 0, 0, 0),
|
||||
"FORCEUNIT": (1, 1, -2, 0, 0, 0, 0),
|
||||
"FREQUENCYUNIT": (0, 0, -1, 0, 0, 0, 0),
|
||||
# "ILLUMINANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
# "INDUCTANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
"LENGTHUNIT": (1, 0, 0, 0, 0, 0, 0),
|
||||
# "LUMINOUSFLUXUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
"LUMINOUSINTENSITYUNIT": (0, 0, 0, 0, 0, 0, 1),
|
||||
# "MAGNETICFLUXDENSITYUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
# "MAGNETICFLUXUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
"MASSUNIT": (0, 1, 0, 0, 0, 0, 0),
|
||||
"PLANEANGLEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
"POWERUNIT": (2, 1, -3, 0, 0, 0, 0),
|
||||
"PRESSUREUNIT": (-1, 1, -2, 0, 0, 0, 0),
|
||||
# "RADIOACTIVITYUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
"SOLIDANGLEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||
"THERMODYNAMICTEMPERATUREUNIT": (0, 0, 0, 0, 1, 0, 0),
|
||||
"TIMEUNIT": (0, 0, 1, 0, 0, 0, 0),
|
||||
"VOLUMEUNIT": (3, 0, 0, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
si_conversions = {
|
||||
"inch": 0.0254,
|
||||
"foot": 0.3048,
|
||||
@@ -169,7 +234,7 @@ def get_prefix_multiplier(text):
|
||||
def get_unit_name(text):
|
||||
text = text.upper().replace("METER", "METRE")
|
||||
for name in unit_names:
|
||||
if name in text:
|
||||
if name.replace("_", " ") in text:
|
||||
return name
|
||||
|
||||
|
||||
@@ -177,6 +242,10 @@ def get_si_dimensions(name):
|
||||
return si_dimensions.get(name, si_dimensions["OTHERWISE"])
|
||||
|
||||
|
||||
def get_named_dimensions(name):
|
||||
return named_dimensions.get(name, (0, 0, 0, 0, 0, 0, 0))
|
||||
|
||||
|
||||
def get_property_unit(prop, ifc_file):
|
||||
unit = getattr(prop, "Unit", None)
|
||||
if unit:
|
||||
@@ -197,6 +266,10 @@ def get_property_unit(prop, ifc_file):
|
||||
return units[0]
|
||||
|
||||
|
||||
def get_unit_measure_type(unit_type):
|
||||
return "Ifc" + unit_type[0:-4].lower().capitalize() + "Measure"
|
||||
|
||||
|
||||
def get_symbol_quantity_class(symbol):
|
||||
# Dumb, but everybody gets it, unlike regex golf
|
||||
if not symbol:
|
||||
@@ -259,19 +332,17 @@ def convert(value, from_prefix, from_unit, to_prefix, to_unit):
|
||||
return value
|
||||
|
||||
|
||||
"""Returns a unit scale factor to convert to and from IFC project length units and SI meters
|
||||
|
||||
Example::
|
||||
|
||||
ifc_project_length * unit_scale = si_meters
|
||||
si_meters / unit_scale = ifc_project_length
|
||||
|
||||
:returns: The scale factor
|
||||
:rtype: float
|
||||
"""
|
||||
|
||||
|
||||
def calculate_unit_scale(file):
|
||||
"""Returns a unit scale factor to convert to and from IFC project length units and SI meters
|
||||
|
||||
Example::
|
||||
|
||||
ifc_project_length * unit_scale = si_meters
|
||||
si_meters / unit_scale = ifc_project_length
|
||||
|
||||
:returns: The scale factor
|
||||
:rtype: float
|
||||
"""
|
||||
units = file.by_type("IfcUnitAssignment")[0]
|
||||
unit_scale = 1
|
||||
for unit in units.Units:
|
||||
|
||||
@@ -34,7 +34,9 @@ class json_logger:
|
||||
self.instance = instance
|
||||
|
||||
def log(self, level, message, *args, **kwargs):
|
||||
self.statements.append(log_entry_type(level, message % args, kwargs.get("instance"))._asdict())
|
||||
self.statements.append(
|
||||
log_entry_type(level, message % args, kwargs.get("instance"))._asdict()
|
||||
)
|
||||
|
||||
def __getattr__(self, level):
|
||||
return functools.partial(self.log, level, instance=self.instance)
|
||||
@@ -81,7 +83,9 @@ def assert_valid(attr, val, schema):
|
||||
if isinstance(attr_type, simple_type):
|
||||
invalid = type(val) != simple_type_python_mapping[attr_type.declared_type()]
|
||||
elif isinstance(attr_type, (entity_type, type_declaration)):
|
||||
invalid = not isinstance(val, ifcopenshell.entity_instance) or not val.is_a(attr_type.name())
|
||||
invalid = not isinstance(val, ifcopenshell.entity_instance) or not val.is_a(
|
||||
attr_type.name()
|
||||
)
|
||||
elif isinstance(attr_type, select_type):
|
||||
val_to_use = val
|
||||
if isinstance(schema.declaration_by_name(val.is_a()), enumeration_type):
|
||||
@@ -90,13 +94,19 @@ def assert_valid(attr, val, schema):
|
||||
else:
|
||||
invalid = True
|
||||
if not invalid:
|
||||
invalid = not any(try_valid(x, val_to_use, schema) for x in attr_type.select_list())
|
||||
invalid = not any(
|
||||
try_valid(x, val_to_use, schema) for x in attr_type.select_list()
|
||||
)
|
||||
elif isinstance(attr_type, enumeration_type):
|
||||
invalid = val not in attr_type.enumeration_items()
|
||||
elif isinstance(attr_type, aggregation_type):
|
||||
b1, b2 = attr_type.bound1(), attr_type.bound2()
|
||||
ty = attr_type.type_of_element()
|
||||
invalid = len(val) < b1 or (b2 != -1 and len(val) > b2) or not all(assert_valid(ty, v, schema) for v in val)
|
||||
invalid = (
|
||||
len(val) < b1
|
||||
or (b2 != -1 and len(val) > b2)
|
||||
or not all(assert_valid(ty, v, schema) for v in val)
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError("Not impl %s %s" % (type(attr_type), attr_type))
|
||||
|
||||
@@ -135,6 +145,7 @@ def validate(f, logger):
|
||||
logger.set_instance(inst)
|
||||
|
||||
entity = schema.declaration_by_name(inst.is_a())
|
||||
attrs = entity.all_attributes()
|
||||
|
||||
if entity.is_abstract():
|
||||
e = "Entity %s is abstract" % entity.name()
|
||||
@@ -143,20 +154,38 @@ def validate(f, logger):
|
||||
else:
|
||||
logger.error("In %s\n%s", inst, e)
|
||||
|
||||
for attr, val, is_derived in zip(entity.all_attributes(), inst, entity.derived()):
|
||||
has_invalid_value = False
|
||||
for i in range(len(attrs)):
|
||||
try:
|
||||
inst[i]
|
||||
pass
|
||||
except:
|
||||
if hasattr(logger, "set_instance"):
|
||||
logger.error("Invalid attribute value for %s.%s", entity, attrs[i])
|
||||
else:
|
||||
logger.error(
|
||||
"In %s\nInvalid attribute value for %s.%s",
|
||||
inst,
|
||||
entity,
|
||||
attrs[i],
|
||||
)
|
||||
has_invalid_value = True
|
||||
|
||||
if val is None and not (is_derived or attr.optional()):
|
||||
logger.error("Attribute %s.%s not optional", entity, attr)
|
||||
if not has_invalid_value:
|
||||
for attr, val, is_derived in zip(attrs, inst, entity.derived()):
|
||||
|
||||
if val is not None:
|
||||
attr_type = attr.type_of_attribute()
|
||||
try:
|
||||
assert_valid(attr, val, schema)
|
||||
except ValidationError as e:
|
||||
if hasattr(logger, "set_instance"):
|
||||
logger.error(str(e))
|
||||
else:
|
||||
logger.error("In %s\n%s", inst, e)
|
||||
if val is None and not (is_derived or attr.optional()):
|
||||
logger.error("Attribute %s.%s not optional", entity, attr)
|
||||
|
||||
if val is not None:
|
||||
attr_type = attr.type_of_attribute()
|
||||
try:
|
||||
assert_valid(attr, val, schema)
|
||||
except ValidationError as e:
|
||||
if hasattr(logger, "set_instance"):
|
||||
logger.error(str(e))
|
||||
else:
|
||||
logger.error("In %s\n%s", inst, e)
|
||||
|
||||
for attr in entity.all_inverse_attributes():
|
||||
val = getattr(inst, attr.name())
|
||||
|
||||
Reference in New Issue
Block a user