mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 06:32:09 +00:00
Fix #4815. Purge deprecated old selector syntax.
This commit is contained in:
@@ -31,7 +31,6 @@ classes = (
|
|||||||
operator.SelectGroupProducts,
|
operator.SelectGroupProducts,
|
||||||
operator.ToggleGroup,
|
operator.ToggleGroup,
|
||||||
operator.UnassignGroup,
|
operator.UnassignGroup,
|
||||||
operator.UpdateGroup,
|
|
||||||
operator.SelectGroupElements,
|
operator.SelectGroupElements,
|
||||||
prop.ExpandedGroups,
|
prop.ExpandedGroups,
|
||||||
prop.Group,
|
prop.Group,
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import ifcopenshell.api
|
|||||||
import blenderbim.bim.helper
|
import blenderbim.bim.helper
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from ifcopenshell.util.selector import Selector
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +47,6 @@ class LoadGroups(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
new = self.props.groups.add()
|
new = self.props.groups.add()
|
||||||
new.ifc_definition_id = group.id()
|
new.ifc_definition_id = group.id()
|
||||||
new.name = group.Name or "Unnamed"
|
new.name = group.Name or "Unnamed"
|
||||||
new.selection_query = ""
|
|
||||||
new.tree_depth = tree_depth
|
new.tree_depth = tree_depth
|
||||||
new.has_children = False
|
new.has_children = False
|
||||||
new.is_expanded = group.id() in self.expanded_groups
|
new.is_expanded = group.id() in self.expanded_groups
|
||||||
@@ -113,7 +111,6 @@ class EditGroup(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.edit_group"
|
bl_idname = "bim.edit_group"
|
||||||
bl_label = "Edit Group"
|
bl_label = "Edit Group"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
copy_from_selector: bpy.props.BoolProperty(name="Copy from Selector", default=False)
|
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = context.scene.BIMGroupProperties
|
props = context.scene.BIMGroupProperties
|
||||||
@@ -124,9 +121,6 @@ class EditGroup(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
else:
|
else:
|
||||||
attributes[attribute.name] = attribute.string_value
|
attributes[attribute.name] = attribute.string_value
|
||||||
|
|
||||||
if self.copy_from_selector:
|
|
||||||
attributes["Description"] = context.scene.IFCSelector.selection_query
|
|
||||||
|
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"group.edit_group", self.file, **{"group": self.file.by_id(props.active_group_id), "attributes": attributes}
|
"group.edit_group", self.file, **{"group": self.file.by_id(props.active_group_id), "attributes": attributes}
|
||||||
@@ -242,31 +236,6 @@ class SelectGroupProducts(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class UpdateGroup(bpy.types.Operator, tool.Ifc.Operator):
|
|
||||||
bl_idname = "bim.update_group"
|
|
||||||
bl_label = "Update Group"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
|
||||||
query: bpy.props.StringProperty()
|
|
||||||
group_id: bpy.props.IntProperty()
|
|
||||||
|
|
||||||
def _execute(self, context):
|
|
||||||
self.file = IfcStore.get_file()
|
|
||||||
group = self.file.by_id(self.group_id)
|
|
||||||
query = self.query
|
|
||||||
|
|
||||||
new_products = Selector.parse(self.file, query)
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"group.update_group_products",
|
|
||||||
self.file,
|
|
||||||
**{
|
|
||||||
"group": group,
|
|
||||||
"products": new_products,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
bpy.ops.bim.load_groups()
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class SelectGroupElements(bpy.types.Operator):
|
class SelectGroupElements(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_group_elements"
|
bl_idname = "bim.select_group_elements"
|
||||||
bl_label = "Select Group elements"
|
bl_label = "Select Group elements"
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class ExpandedGroups(StrProperty):
|
|||||||
class Group(PropertyGroup):
|
class Group(PropertyGroup):
|
||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
selection_query: StringProperty(name="Selection Query")
|
|
||||||
is_expanded: BoolProperty(name="Is Expanded", default=False)
|
is_expanded: BoolProperty(name="Is Expanded", default=False)
|
||||||
has_children: BoolProperty(name="Has Children", default=False)
|
has_children: BoolProperty(name="Has Children", default=False)
|
||||||
tree_depth: IntProperty(name="Tree Depth")
|
tree_depth: IntProperty(name="Tree Depth")
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class BIM_UL_groups(UIList):
|
|||||||
else:
|
else:
|
||||||
row.label(text="", icon="BLANK1")
|
row.label(text="", icon="BLANK1")
|
||||||
|
|
||||||
row.label(text=f"*{item.name}") if item.selection_query != "" else row.label(text=item.name)
|
row.label(text=item.name)
|
||||||
group_id = item.ifc_definition_id
|
group_id = item.ifc_definition_id
|
||||||
if context.scene.BIMGroupProperties.active_group_id == group_id:
|
if context.scene.BIMGroupProperties.active_group_id == group_id:
|
||||||
op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF")
|
op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF")
|
||||||
@@ -145,10 +145,6 @@ class BIM_UL_groups(UIList):
|
|||||||
op.group = group_id
|
op.group = group_id
|
||||||
op = row.operator("bim.remove_group", text="", icon="X")
|
op = row.operator("bim.remove_group", text="", icon="X")
|
||||||
op.group = group_id
|
op.group = group_id
|
||||||
if item.selection_query != "":
|
|
||||||
op = row.operator("bim.update_group", text="", icon="FILE_REFRESH")
|
|
||||||
op.group_id = item.ifc_definition_id
|
|
||||||
op.query = item.selection_query
|
|
||||||
else:
|
else:
|
||||||
op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF")
|
op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF")
|
||||||
op.group = group_id
|
op.group = group_id
|
||||||
@@ -158,10 +154,6 @@ class BIM_UL_groups(UIList):
|
|||||||
op.group = group_id
|
op.group = group_id
|
||||||
op = row.operator("bim.remove_group", text="", icon="X")
|
op = row.operator("bim.remove_group", text="", icon="X")
|
||||||
op.group = group_id
|
op.group = group_id
|
||||||
if item.selection_query != "":
|
|
||||||
op = row.operator("bim.update_group", text="", icon="FILE_REFRESH")
|
|
||||||
op.group_id = item.ifc_definition_id
|
|
||||||
op.query = item.selection_query
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_UL_object_groups(UIList):
|
class BIM_UL_object_groups(UIList):
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import re
|
|
||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
@@ -24,10 +23,8 @@ import ifcopenshell.api
|
|||||||
import ifcopenshell.guid
|
import ifcopenshell.guid
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
from ifcopenshell.util.selector import Selector
|
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.group import ui
|
|
||||||
import blenderbim.core.search as core
|
import blenderbim.core.search as core
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
from bpy.types import PropertyGroup, Operator
|
from bpy.types import PropertyGroup, Operator
|
||||||
|
|||||||
@@ -19,12 +19,9 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from ifcopenshell import util
|
from ifcopenshell import util
|
||||||
from ifcopenshell.util.selector import Selector
|
from blenderbim.bim.prop import ObjProperty, BIMFilterGroup
|
||||||
from blenderbim.bim.prop import ObjProperty, StrProperty, BIMFilterGroup
|
|
||||||
from blenderbim.bim.ifc import IfcStore
|
|
||||||
from blenderbim.bim.module.search.data import SearchData, ColourByPropertyData, SelectSimilarData
|
from blenderbim.bim.module.search.data import SearchData, ColourByPropertyData, SelectSimilarData
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from blenderbim.tool.ifc import Ifc
|
|
||||||
from . import ui, prop, operator
|
from . import ui, prop, operator
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
PointerProperty,
|
PointerProperty,
|
||||||
|
|||||||
@@ -1,10 +1,2 @@
|
|||||||
def show_scene_elements(spatial):
|
def show_scene_elements(spatial):
|
||||||
spatial.show_scene_objects()
|
spatial.show_scene_objects()
|
||||||
|
|
||||||
def search(search, spatial, query, action):
|
|
||||||
products = search.from_selector_query(query)
|
|
||||||
spatial.deselect_objects()
|
|
||||||
try:
|
|
||||||
spatial.filter_products(products, action)
|
|
||||||
except:
|
|
||||||
return "One or More Products could not be found because they are hidden in the ViewLayer"
|
|
||||||
@@ -707,7 +707,7 @@ class Selector:
|
|||||||
|
|
||||||
@interface
|
@interface
|
||||||
class Search:
|
class Search:
|
||||||
def from_selector_query(cls, query): pass
|
pass
|
||||||
|
|
||||||
@interface
|
@interface
|
||||||
class Sequence:
|
class Sequence:
|
||||||
@@ -831,7 +831,7 @@ class Spatial:
|
|||||||
def can_reference(cls, structure, element): pass
|
def can_reference(cls, structure, element): pass
|
||||||
def contract_container(cls, container): pass
|
def contract_container(cls, container): pass
|
||||||
def copy_xy(cls, src_obj, destination_obj): pass
|
def copy_xy(cls, src_obj, destination_obj): pass
|
||||||
def import_spatial_structure(cls, element, level_index): pass
|
def import_spatial_element(cls, element, level_index): pass
|
||||||
def deselect_objects(cls): pass
|
def deselect_objects(cls): pass
|
||||||
def disable_editing(cls, obj): pass
|
def disable_editing(cls, obj): pass
|
||||||
def duplicate_object_and_data(cls, obj): pass
|
def duplicate_object_and_data(cls, obj): pass
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ import bpy
|
|||||||
import json
|
import json
|
||||||
import lark
|
import lark
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.tool as tool
|
|
||||||
import ifcopenshell.guid
|
import ifcopenshell.guid
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
from ifcopenshell.util.selector import Selector
|
|
||||||
from blenderbim.bim.prop import BIMFacet
|
from blenderbim.bim.prop import BIMFacet
|
||||||
from typing import Union, Literal
|
from typing import Union, Literal
|
||||||
|
|
||||||
@@ -115,11 +113,6 @@ class Search(blenderbim.core.tool.Search):
|
|||||||
return value
|
return value
|
||||||
return '"' + value.replace('"', '\\"') + '"'
|
return '"' + value.replace('"', '\\"') + '"'
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_selector_query(cls, query: str) -> list[ifcopenshell.entity_instance]:
|
|
||||||
"""Returns a list of products from a selector query"""
|
|
||||||
return Selector().parse(tool.Ifc.get(), query)
|
|
||||||
|
|
||||||
|
|
||||||
class ImportFilterQueryTransformer(lark.Transformer):
|
class ImportFilterQueryTransformer(lark.Transformer):
|
||||||
def __init__(self, filter_groups):
|
def __init__(self, filter_groups):
|
||||||
|
|||||||
@@ -276,7 +276,130 @@ def format(query: str) -> str:
|
|||||||
|
|
||||||
def get_element_value(element: ifcopenshell.entity_instance, query: str) -> Any:
|
def get_element_value(element: ifcopenshell.entity_instance, query: str) -> Any:
|
||||||
keys: list[str] = GetElementTransformer().transform(get_element_grammar.parse(query))
|
keys: list[str] = GetElementTransformer().transform(get_element_grammar.parse(query))
|
||||||
return Selector.get_element_value(element, keys)
|
return _get_element_value(element, keys)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_element_value(element: ifcopenshell.entity_instance, keys: list[str]) -> Any:
|
||||||
|
value = element
|
||||||
|
for key in keys:
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
|
if key == "type":
|
||||||
|
value = ifcopenshell.util.element.get_type(value)
|
||||||
|
elif key in ("material", "mat"):
|
||||||
|
value = ifcopenshell.util.element.get_material(value, should_skip_usage=True)
|
||||||
|
elif key in ("materials", "mats"):
|
||||||
|
value = ifcopenshell.util.element.get_materials(value)
|
||||||
|
elif key == "profiles":
|
||||||
|
value = ifcopenshell.util.shape.get_profiles(value)
|
||||||
|
elif key == "styles":
|
||||||
|
value = ifcopenshell.util.element.get_styles(value)
|
||||||
|
elif key in ("item", "i"):
|
||||||
|
if value.is_a("IfcMaterialLayerSet"):
|
||||||
|
value = value.MaterialLayers
|
||||||
|
elif value.is_a("IfcMaterialProfileSet"):
|
||||||
|
value = value.MaterialProfiles
|
||||||
|
elif value.is_a("IfcMaterialConstituentSet"):
|
||||||
|
value = value.MaterialConstituents
|
||||||
|
elif key == "container":
|
||||||
|
value = ifcopenshell.util.element.get_container(value)
|
||||||
|
elif key == "space":
|
||||||
|
value = ifcopenshell.util.element.get_container(value, ifc_class="IfcSpace")
|
||||||
|
elif key == "storey":
|
||||||
|
value = ifcopenshell.util.element.get_container(value, ifc_class="IfcBuildingStorey")
|
||||||
|
elif key == "building":
|
||||||
|
value = ifcopenshell.util.element.get_container(value, ifc_class="IfcBuilding")
|
||||||
|
elif key == "site":
|
||||||
|
value = ifcopenshell.util.element.get_container(value, ifc_class="IfcSite")
|
||||||
|
elif key == "parent":
|
||||||
|
value = ifcopenshell.util.element.get_parent(value)
|
||||||
|
elif key in ("types", "occurrences"):
|
||||||
|
value = ifcopenshell.util.element.get_types(value)
|
||||||
|
elif key == "count":
|
||||||
|
if isinstance(value, set):
|
||||||
|
value = len(list(value))
|
||||||
|
elif isinstance(value, (list, tuple)):
|
||||||
|
value = len(value)
|
||||||
|
else:
|
||||||
|
value = 1
|
||||||
|
elif key == "class":
|
||||||
|
value = value.is_a()
|
||||||
|
elif key == "predefined_type":
|
||||||
|
value = ifcopenshell.util.element.get_predefined_type(value)
|
||||||
|
elif key == "id":
|
||||||
|
value = value.id()
|
||||||
|
elif key == "classification":
|
||||||
|
value = ifcopenshell.util.classification.get_references(value)
|
||||||
|
elif key in ("x", "y", "z", "easting", "northing", "elevation") and hasattr(value, "ObjectPlacement"):
|
||||||
|
if getattr(value, "ObjectPlacement", None):
|
||||||
|
matrix = ifcopenshell.util.placement.get_local_placement(value.ObjectPlacement)
|
||||||
|
xyz = matrix[:, 3][:3]
|
||||||
|
if key in ("x", "y", "z"):
|
||||||
|
value = xyz["xyz".index(key)]
|
||||||
|
else:
|
||||||
|
enh = ifcopenshell.util.geolocation.auto_xyz2enh(element.wrapped_data.file, *xyz)
|
||||||
|
value = enh[("easting", "northing", "elevation").index(key)]
|
||||||
|
else:
|
||||||
|
value = None
|
||||||
|
elif isinstance(value, ifcopenshell.entity_instance):
|
||||||
|
if key == "Name" and value.is_a("IfcMaterialLayerSet"):
|
||||||
|
key = "LayerSetName" # This oddity in the IFC spec is annoying so we account for it.
|
||||||
|
|
||||||
|
if isinstance(key, re.Pattern):
|
||||||
|
attribute = None # Should we support regex attributes? Probably not for now.
|
||||||
|
else:
|
||||||
|
attribute = getattr(value, key, None)
|
||||||
|
|
||||||
|
if attribute is not None:
|
||||||
|
value = attribute
|
||||||
|
else:
|
||||||
|
# Try to extract pset
|
||||||
|
if isinstance(key, re.Pattern):
|
||||||
|
psets = ifcopenshell.util.element.get_psets(value)
|
||||||
|
matching_psets = []
|
||||||
|
for pset_name, pset in psets.items():
|
||||||
|
if key.match(pset_name):
|
||||||
|
del pset["id"]
|
||||||
|
matching_psets.append(pset)
|
||||||
|
result = matching_psets or None
|
||||||
|
if result and len(result) == 1:
|
||||||
|
result = result[0]
|
||||||
|
else:
|
||||||
|
result = ifcopenshell.util.element.get_pset(value, key)
|
||||||
|
if result:
|
||||||
|
del result["id"]
|
||||||
|
|
||||||
|
value = result
|
||||||
|
elif isinstance(value, dict): # Such as from the result of a prior get_pset
|
||||||
|
if isinstance(key, re.Pattern):
|
||||||
|
results = []
|
||||||
|
for prop_name, prop_value in value.items():
|
||||||
|
if key.match(prop_name):
|
||||||
|
if isinstance(prop_value, (list, tuple)):
|
||||||
|
results.extend(prop_value)
|
||||||
|
else:
|
||||||
|
results.append(prop_value)
|
||||||
|
value = results or None
|
||||||
|
if value and len(value) == 1:
|
||||||
|
value = value[0]
|
||||||
|
else:
|
||||||
|
value = value.get(key, None)
|
||||||
|
elif isinstance(value, (list, tuple, set)): # If we use regex
|
||||||
|
if isinstance(key, str) and key.isnumeric():
|
||||||
|
try:
|
||||||
|
value = value[int(key)]
|
||||||
|
except IndexError:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
results = []
|
||||||
|
for v in value:
|
||||||
|
subvalue = _get_element_value(v, [key])
|
||||||
|
if isinstance(subvalue, list):
|
||||||
|
results.extend(subvalue)
|
||||||
|
else:
|
||||||
|
results.append(subvalue)
|
||||||
|
value = results
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def filter_elements(
|
def filter_elements(
|
||||||
@@ -840,368 +963,3 @@ class FacetTransformer(lark.Transformer):
|
|||||||
if comparison.startswith("!"):
|
if comparison.startswith("!"):
|
||||||
return not result
|
return not result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class Selector:
|
|
||||||
@classmethod
|
|
||||||
def parse(
|
|
||||||
cls, ifc_file: ifcopenshell.file, query: str, elements: Optional[list[ifcopenshell.entity_instance]] = None
|
|
||||||
) -> list[ifcopenshell.entity_instance]:
|
|
||||||
cls.file = ifc_file
|
|
||||||
cls.elements = elements
|
|
||||||
l = lark.Lark(
|
|
||||||
"""start: query (lfunction query)*
|
|
||||||
query: selector | group
|
|
||||||
group: "(" query (lfunction query)* ")"
|
|
||||||
selector: (inverse_relationship)? guid_selector | (inverse_relationship)? class_selector
|
|
||||||
guid_selector: "#" /[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$]{22}/
|
|
||||||
class_selector: "." WORD filter ?
|
|
||||||
filter: "[" filter_key (comparison filter_value)? "]"
|
|
||||||
filter_key: WORD | ESCAPED_STRING | keys_regex | keys_quoted | keys_simple
|
|
||||||
filter_value: filter_regex | ESCAPED_STRING | SIGNED_FLOAT | SIGNED_INT | BOOLEAN | NULL
|
|
||||||
filter_regex: "r" ESCAPED_STRING
|
|
||||||
keys_regex: "r" ESCAPED_STRING ("." ESCAPED_STRING)*
|
|
||||||
keys_quoted: ESCAPED_STRING ("." ESCAPED_STRING)*
|
|
||||||
keys_simple: /[^\\W][^.=<>!%*\\]]*/ ("." /[^\\W][^.=<>!%*\\]]*/)*
|
|
||||||
lfunction: and | or
|
|
||||||
inverse_relationship: types | decomposed_by | bounded_by | grouped_by
|
|
||||||
types: "*"
|
|
||||||
decomposed_by: "@"
|
|
||||||
bounded_by: "@@"
|
|
||||||
grouped_by: "@@@"
|
|
||||||
and: "&"
|
|
||||||
or: "|"
|
|
||||||
not: "!"
|
|
||||||
comparison: (not)* (oneof | contains | morethanequalto | lessthanequalto | equal | morethan | lessthan)
|
|
||||||
oneof: "%="
|
|
||||||
contains: "*="
|
|
||||||
morethanequalto: ">="
|
|
||||||
lessthanequalto: "<="
|
|
||||||
equal: "="
|
|
||||||
morethan: ">"
|
|
||||||
lessthan: "<"
|
|
||||||
BOOLEAN: "TRUE" | "FALSE" | "true" | "false"| "True" | "False"
|
|
||||||
NULL: "NULL"
|
|
||||||
|
|
||||||
// Embed common.lark for packaging
|
|
||||||
DIGIT: "0".."9"
|
|
||||||
HEXDIGIT: "a".."f"|"A".."F"|DIGIT
|
|
||||||
INT: DIGIT+
|
|
||||||
SIGNED_INT: ["+"|"-"] INT
|
|
||||||
DECIMAL: INT "." INT? | "." INT
|
|
||||||
_EXP: ("e"|"E") SIGNED_INT
|
|
||||||
FLOAT: INT _EXP | DECIMAL _EXP?
|
|
||||||
SIGNED_FLOAT: ["+"|"-"] FLOAT
|
|
||||||
NUMBER: FLOAT | INT
|
|
||||||
SIGNED_NUMBER: ["+"|"-"] NUMBER
|
|
||||||
_STRING_INNER: /.*?/
|
|
||||||
_STRING_ESC_INNER: _STRING_INNER /(?<!\\\\)(\\\\\\\\)*?/
|
|
||||||
ESCAPED_STRING : "\\"" _STRING_ESC_INNER "\\""
|
|
||||||
LCASE_LETTER: "a".."z"
|
|
||||||
UCASE_LETTER: "A".."Z"
|
|
||||||
LETTER: UCASE_LETTER | LCASE_LETTER
|
|
||||||
WORD: LETTER+
|
|
||||||
CNAME: ("_"|LETTER) ("_"|LETTER|DIGIT)*
|
|
||||||
WS_INLINE: (" "|/\\t/)+
|
|
||||||
WS: /[ \\t\\f\\r\\n]/+
|
|
||||||
CR : /\\r/
|
|
||||||
LF : /\\n/
|
|
||||||
NEWLINE: (CR? LF)+
|
|
||||||
|
|
||||||
%ignore WS // Disregard spaces in text
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
start = l.parse(query)
|
|
||||||
return cls.get_group(start)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_group(cls, group):
|
|
||||||
lfunction = None
|
|
||||||
for child in group.children:
|
|
||||||
if child.data == "query":
|
|
||||||
new_results = cls.get_query(child)
|
|
||||||
if not lfunction:
|
|
||||||
results = new_results
|
|
||||||
elif lfunction == "or":
|
|
||||||
results.extend(new_results)
|
|
||||||
elif lfunction == "and":
|
|
||||||
results = list(set(results).intersection(new_results))
|
|
||||||
results = list(set(results))
|
|
||||||
elif child.data == "lfunction":
|
|
||||||
lfunction = child.children[0].data
|
|
||||||
return results
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_query(cls, query):
|
|
||||||
for child in query.children:
|
|
||||||
if child.data == "selector":
|
|
||||||
return cls.get_selector(child)
|
|
||||||
elif child.data == "group":
|
|
||||||
return cls.get_group(child)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_selector(cls, selector):
|
|
||||||
if len(selector.children) == 1:
|
|
||||||
inverse_relationship = None
|
|
||||||
class_or_guid_selector = selector.children[0]
|
|
||||||
else:
|
|
||||||
inverse_relationship = selector.children[0]
|
|
||||||
class_or_guid_selector = selector.children[1]
|
|
||||||
|
|
||||||
if class_or_guid_selector.data == "class_selector":
|
|
||||||
results = cls.get_class_selector(class_or_guid_selector)
|
|
||||||
elif class_or_guid_selector.data == "guid_selector":
|
|
||||||
results = cls.get_guid_selector(class_or_guid_selector)
|
|
||||||
|
|
||||||
if not inverse_relationship:
|
|
||||||
return results
|
|
||||||
return cls.parse_inverse_relationship(results, inverse_relationship.children[0].data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse_inverse_relationship(cls, elements, inverse_relationship):
|
|
||||||
results = []
|
|
||||||
for element in elements:
|
|
||||||
if inverse_relationship == "types":
|
|
||||||
if hasattr(element, "Types") and element.Types:
|
|
||||||
results.extend(element.Types[0].RelatedObjects)
|
|
||||||
elif hasattr(element, "ObjectTypeOf") and element.ObjectTypeOf:
|
|
||||||
results.extend(element.ObjectTypeOf[0].RelatedObjects)
|
|
||||||
elif inverse_relationship == "decomposed_by":
|
|
||||||
results.extend(ifcopenshell.util.element.get_decomposition(element))
|
|
||||||
elif inverse_relationship == "grouped_by":
|
|
||||||
results.extend(ifcopenshell.util.element.get_grouped_by(element))
|
|
||||||
elif inverse_relationship == "bounded_by" and hasattr(element, "BoundedBy"):
|
|
||||||
for relationship in element.BoundedBy:
|
|
||||||
results.append(relationship.RelatedBuildingElement)
|
|
||||||
return results
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_class_selector(cls, class_selector):
|
|
||||||
if class_selector.children[0] == "COBie":
|
|
||||||
elements = ifcopenshell.util.fm.get_cobie_components(cls.file)
|
|
||||||
elif class_selector.children[0] == "COBieType":
|
|
||||||
elements = ifcopenshell.util.fm.get_cobie_types(cls.file)
|
|
||||||
elif class_selector.children[0] == "FMHEM":
|
|
||||||
elements = ifcopenshell.util.fm.get_fmhem_types(cls.file)
|
|
||||||
else:
|
|
||||||
if cls.elements is None:
|
|
||||||
elements = cls.file.by_type(class_selector.children[0])
|
|
||||||
else:
|
|
||||||
elements = [e for e in cls.elements if e.is_a(class_selector.children[0])]
|
|
||||||
if len(class_selector.children) > 1 and class_selector.children[1].data == "filter":
|
|
||||||
return cls.filter_elements(elements, class_selector.children[1])
|
|
||||||
return elements
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def filter_elements(cls, elements, filter_rule):
|
|
||||||
results = []
|
|
||||||
filter_query = cls.parse_filter_query(filter_rule.children[0].children[0])
|
|
||||||
comparison = value = None
|
|
||||||
if len(filter_rule.children) > 1:
|
|
||||||
comparison = filter_rule.children[1].children[0].data
|
|
||||||
if comparison == "not":
|
|
||||||
comparison += filter_rule.children[1].children[1].data
|
|
||||||
filter_value = filter_rule.children[2].children[0]
|
|
||||||
if isinstance(filter_value, lark.Tree):
|
|
||||||
is_regex = True
|
|
||||||
token_type = filter_value.data
|
|
||||||
else:
|
|
||||||
is_regex = False
|
|
||||||
token_type = filter_value.type
|
|
||||||
if token_type == "filter_regex":
|
|
||||||
value = str(filter_value.children[0][1:-1])
|
|
||||||
elif token_type == "ESCAPED_STRING":
|
|
||||||
value = str(filter_value[1:-1])
|
|
||||||
elif token_type == "SIGNED_INT":
|
|
||||||
value = int(filter_value)
|
|
||||||
elif token_type == "SIGNED_FLOAT":
|
|
||||||
value = float(filter_value)
|
|
||||||
elif token_type == "BOOLEAN":
|
|
||||||
value = filter_value.lower() == "true"
|
|
||||||
elif token_type == "NULL":
|
|
||||||
value = None
|
|
||||||
for element in elements:
|
|
||||||
if filter_query["is_regex"]:
|
|
||||||
filter_query["keys"] = [re.compile(k) for k in filter_query["keys"]]
|
|
||||||
element_value = cls.get_element_value(element, filter_query["keys"])
|
|
||||||
if element_value is None and value is not None and "not" not in comparison:
|
|
||||||
continue
|
|
||||||
if comparison and cls.filter_element(element, element_value, comparison, value, is_regex=is_regex):
|
|
||||||
results.append(element)
|
|
||||||
elif not comparison and element_value:
|
|
||||||
results.append(element)
|
|
||||||
return results
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse_filter_query(cls, filter_query):
|
|
||||||
keys = filter_query
|
|
||||||
is_regex = False
|
|
||||||
if isinstance(keys, str):
|
|
||||||
keys = [keys]
|
|
||||||
elif keys.data == "keys_regex":
|
|
||||||
is_regex = True
|
|
||||||
keys = [k[1:-1].replace('\\"', '"') for k in keys.children]
|
|
||||||
elif keys.data == "keys_quoted":
|
|
||||||
keys = [k[1:-1].replace('\\"', '"') for k in keys.children]
|
|
||||||
elif keys.data == "keys_simple":
|
|
||||||
keys = keys.children
|
|
||||||
return {"keys": keys, "is_regex": is_regex}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_element_value(cls, element: ifcopenshell.entity_instance, keys: list[str]) -> Any:
|
|
||||||
value = element
|
|
||||||
for key in keys:
|
|
||||||
if value is None:
|
|
||||||
return
|
|
||||||
if key == "type":
|
|
||||||
value = ifcopenshell.util.element.get_type(value)
|
|
||||||
elif key in ("material", "mat"):
|
|
||||||
value = ifcopenshell.util.element.get_material(value, should_skip_usage=True)
|
|
||||||
elif key in ("materials", "mats"):
|
|
||||||
value = ifcopenshell.util.element.get_materials(value)
|
|
||||||
elif key == "profiles":
|
|
||||||
value = ifcopenshell.util.shape.get_profiles(value)
|
|
||||||
elif key == "styles":
|
|
||||||
value = ifcopenshell.util.element.get_styles(value)
|
|
||||||
elif key in ("item", "i"):
|
|
||||||
if value.is_a("IfcMaterialLayerSet"):
|
|
||||||
value = value.MaterialLayers
|
|
||||||
elif value.is_a("IfcMaterialProfileSet"):
|
|
||||||
value = value.MaterialProfiles
|
|
||||||
elif value.is_a("IfcMaterialConstituentSet"):
|
|
||||||
value = value.MaterialConstituents
|
|
||||||
elif key == "container":
|
|
||||||
value = ifcopenshell.util.element.get_container(value)
|
|
||||||
elif key == "space":
|
|
||||||
value = ifcopenshell.util.element.get_container(value, ifc_class="IfcSpace")
|
|
||||||
elif key == "storey":
|
|
||||||
value = ifcopenshell.util.element.get_container(value, ifc_class="IfcBuildingStorey")
|
|
||||||
elif key == "building":
|
|
||||||
value = ifcopenshell.util.element.get_container(value, ifc_class="IfcBuilding")
|
|
||||||
elif key == "site":
|
|
||||||
value = ifcopenshell.util.element.get_container(value, ifc_class="IfcSite")
|
|
||||||
elif key == "parent":
|
|
||||||
value = ifcopenshell.util.element.get_parent(value)
|
|
||||||
elif key in ("types", "occurrences"):
|
|
||||||
value = ifcopenshell.util.element.get_types(value)
|
|
||||||
elif key == "count":
|
|
||||||
if isinstance(value, set):
|
|
||||||
value = len(list(value))
|
|
||||||
elif isinstance(value, (list, tuple)):
|
|
||||||
value = len(value)
|
|
||||||
else:
|
|
||||||
value = 1
|
|
||||||
elif key == "class":
|
|
||||||
value = value.is_a()
|
|
||||||
elif key == "predefined_type":
|
|
||||||
value = ifcopenshell.util.element.get_predefined_type(value)
|
|
||||||
elif key == "id":
|
|
||||||
value = value.id()
|
|
||||||
elif key == "classification":
|
|
||||||
value = ifcopenshell.util.classification.get_references(value)
|
|
||||||
elif key in ("x", "y", "z", "easting", "northing", "elevation") and hasattr(value, "ObjectPlacement"):
|
|
||||||
if getattr(value, "ObjectPlacement", None):
|
|
||||||
matrix = ifcopenshell.util.placement.get_local_placement(value.ObjectPlacement)
|
|
||||||
xyz = matrix[:, 3][:3]
|
|
||||||
if key in ("x", "y", "z"):
|
|
||||||
value = xyz["xyz".index(key)]
|
|
||||||
else:
|
|
||||||
enh = ifcopenshell.util.geolocation.auto_xyz2enh(element.wrapped_data.file, *xyz)
|
|
||||||
value = enh[("easting", "northing", "elevation").index(key)]
|
|
||||||
else:
|
|
||||||
value = None
|
|
||||||
elif isinstance(value, ifcopenshell.entity_instance):
|
|
||||||
if key == "Name" and value.is_a("IfcMaterialLayerSet"):
|
|
||||||
key = "LayerSetName" # This oddity in the IFC spec is annoying so we account for it.
|
|
||||||
|
|
||||||
if isinstance(key, re.Pattern):
|
|
||||||
attribute = None # Should we support regex attributes? Probably not for now.
|
|
||||||
else:
|
|
||||||
attribute = getattr(value, key, None)
|
|
||||||
|
|
||||||
if attribute is not None:
|
|
||||||
value = attribute
|
|
||||||
else:
|
|
||||||
# Try to extract pset
|
|
||||||
if isinstance(key, re.Pattern):
|
|
||||||
psets = ifcopenshell.util.element.get_psets(value)
|
|
||||||
matching_psets = []
|
|
||||||
for pset_name, pset in psets.items():
|
|
||||||
if key.match(pset_name):
|
|
||||||
del pset["id"]
|
|
||||||
matching_psets.append(pset)
|
|
||||||
result = matching_psets or None
|
|
||||||
if result and len(result) == 1:
|
|
||||||
result = result[0]
|
|
||||||
else:
|
|
||||||
result = ifcopenshell.util.element.get_pset(value, key)
|
|
||||||
if result:
|
|
||||||
del result["id"]
|
|
||||||
|
|
||||||
value = result
|
|
||||||
elif isinstance(value, dict): # Such as from the result of a prior get_pset
|
|
||||||
if isinstance(key, re.Pattern):
|
|
||||||
results = []
|
|
||||||
for prop_name, prop_value in value.items():
|
|
||||||
if key.match(prop_name):
|
|
||||||
if isinstance(prop_value, (list, tuple)):
|
|
||||||
results.extend(prop_value)
|
|
||||||
else:
|
|
||||||
results.append(prop_value)
|
|
||||||
value = results or None
|
|
||||||
if value and len(value) == 1:
|
|
||||||
value = value[0]
|
|
||||||
else:
|
|
||||||
value = value.get(key, None)
|
|
||||||
elif isinstance(value, (list, tuple, set)): # If we use regex
|
|
||||||
if isinstance(key, str) and key.isnumeric():
|
|
||||||
try:
|
|
||||||
value = value[int(key)]
|
|
||||||
except IndexError:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
results = []
|
|
||||||
for v in value:
|
|
||||||
subvalue = cls.get_element_value(v, [key])
|
|
||||||
if isinstance(subvalue, list):
|
|
||||||
results.extend(subvalue)
|
|
||||||
else:
|
|
||||||
results.append(subvalue)
|
|
||||||
value = results
|
|
||||||
return value
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def filter_element(cls, element, element_value, comparison, value, is_regex=False):
|
|
||||||
if comparison.startswith("not"):
|
|
||||||
return not cls.filter_element(element, element_value, comparison[3:], value, is_regex=is_regex)
|
|
||||||
elif comparison == "equal" and isinstance(element_value, list):
|
|
||||||
if is_regex:
|
|
||||||
for element_v in element_value:
|
|
||||||
if re.match(value, element_v):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
return value in element_value
|
|
||||||
elif comparison == "equal":
|
|
||||||
if is_regex:
|
|
||||||
return bool(re.match(value, element_value))
|
|
||||||
return element_value == value
|
|
||||||
elif comparison == "contains" and isinstance(element_value, list):
|
|
||||||
return bool([ev for ev in element_value if value in str(ev)])
|
|
||||||
elif comparison == "contains":
|
|
||||||
return value in str(element_value)
|
|
||||||
elif comparison == "morethan":
|
|
||||||
return element_value > value
|
|
||||||
elif comparison == "lessthan":
|
|
||||||
return element_value < value
|
|
||||||
elif comparison == "morethanequalto":
|
|
||||||
return element_value >= value
|
|
||||||
elif comparison == "lessthanequalto":
|
|
||||||
return element_value <= value
|
|
||||||
elif comparison == "oneof":
|
|
||||||
return element_value in value.split(",")
|
|
||||||
return False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_guid_selector(cls, guid_selector):
|
|
||||||
return [cls.file.by_id(guid_selector.children[0])]
|
|
||||||
|
|||||||
@@ -348,236 +348,3 @@ class TestSetElementValue(test.bootstrap.IFC4):
|
|||||||
layer.Material = material
|
layer.Material = material
|
||||||
subject.set_element_value(self.file, layer, "Material.Name", "Foo")
|
subject.set_element_value(self.file, layer, "Material.Name", "Foo")
|
||||||
assert material.Name == "Foo"
|
assert material.Name == "Foo"
|
||||||
|
|
||||||
|
|
||||||
class TestSelector(test.bootstrap.IFC4):
|
|
||||||
def test_selecting_from_specified_elements(self):
|
|
||||||
elements = [ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") for _ in range(2)]
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcWall", elements[:1]) == [elements[0]]
|
|
||||||
|
|
||||||
def test_selecting_by_class(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSlab")
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcWall") == [element]
|
|
||||||
|
|
||||||
def test_selecting_by_globalid(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSlab")
|
|
||||||
assert subject.Selector.parse(self.file, f"#{element.GlobalId}") == [element]
|
|
||||||
|
|
||||||
def test_selecting_by_attribute_existence(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element.Name = "Foobar"
|
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSlab")
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Name]") == [element]
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Description]") == []
|
|
||||||
|
|
||||||
def test_selecting_by_attribute(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element.Name = "Foobar"
|
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSlab")
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name="Foobar"]') == [element]
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name="Foobaz"]') == []
|
|
||||||
|
|
||||||
def test_selecting_by_regex(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element.Name = "Foobar"
|
|
||||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSlab")
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name=r"Foo.*"]') == [element]
|
|
||||||
|
|
||||||
def test_selecting_by_property_existence(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Bar"})
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo]") == [element]
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Fox]") == []
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[r"Foo.*ar"."Fo.*"]') == [element]
|
|
||||||
|
|
||||||
def test_selecting_by_string_property(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Bar"})
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Foo="Bar"]') == [element]
|
|
||||||
|
|
||||||
def test_selecting_by_enumerated_property(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Pset_WallCommon")
|
|
||||||
template = ifcopenshell.util.pset.get_template("IFC4").get_by_name("Pset_WallCommon")
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"pset.edit_pset", self.file, pset=pset, properties={"Status": ["NEW"]}, pset_template=template
|
|
||||||
)
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Pset_WallCommon.Status="NEW"]') == [element]
|
|
||||||
|
|
||||||
def test_selecting_by_integer_property(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": 42})
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo=42]") == [element]
|
|
||||||
|
|
||||||
def test_selecting_by_float_property(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": 4.2})
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo=4.2]") == [element]
|
|
||||||
|
|
||||||
def test_selecting_by_boolean_property(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": True})
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo=TRUE]") == [element]
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": False})
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo=FALSE]") == [element]
|
|
||||||
|
|
||||||
def test_selecting_by_null_property(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Bar"})
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo=NULL]") == []
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": None})
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo=NULL]") == [element]
|
|
||||||
|
|
||||||
def test_comparing_by_not_equal(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element.Name = "Foobar"
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name!="Foobaz"]') == [element]
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name!="Foobar"]') == []
|
|
||||||
|
|
||||||
def test_comparing_by_ranges(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": 4.2})
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo>2]") == [element]
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo>20]") == []
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo<2]") == []
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo<20]") == [element]
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo>=4.2]") == [element]
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo<=4.2]") == [element]
|
|
||||||
|
|
||||||
def test_comparing_if_value_contains_a_wildcard_string(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element.Name = "Foobar"
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name*="Foo"]') == [element]
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name*="oba"]') == [element]
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name*="abc"]') == []
|
|
||||||
|
|
||||||
def test_selecting_if_value_not_matching(self):
|
|
||||||
element_1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element_2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset_1 = ifcopenshell.api.run("pset.add_pset", self.file, product=element_1, name="Foo_Bar")
|
|
||||||
pset_2 = ifcopenshell.api.run("pset.add_pset", self.file, product=element_2, name="Foo_Bar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset_1, properties={"Foo": "Bar"})
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset_2, properties={"Foo": "BOO"})
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement["Foo_Bar"."Foo" != "Bar"]') == [element_2]
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement["Foo_Bar"."Foo" != "BOO"]') == [element_1]
|
|
||||||
|
|
||||||
def test_selecting_when_attribute_is_none(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[PredefinedType !="non-existent predefined type"]') == [
|
|
||||||
element
|
|
||||||
]
|
|
||||||
|
|
||||||
def test_selecting_a_property_which_includes_non_standard_characters(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="a !%$§&/()?|*-+,€~#@µ^°a")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"a !%$§&/()?|*-+,€~#@µ^°a": "Bar"})
|
|
||||||
assert subject.Selector.parse(
|
|
||||||
self.file, '.IfcElement["a !%$§&/()?|*-+,€~#@µ^°a"."a !%$§&/()?|*-+,€~#@µ^°a"="Bar"]'
|
|
||||||
) == [element]
|
|
||||||
|
|
||||||
def test_selecting_a_property_which_includes_a_dot(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="a.b")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"c.d": "Bar"})
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement["a.b"."c.d"="Bar"]') == [element]
|
|
||||||
|
|
||||||
def test_selecting_a_property_which_includes_an_escaped_quote(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name='"a.b"')
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={'"c.d"': "Bar"})
|
|
||||||
assert subject.Selector.parse(self.file, r'.IfcElement["\"a.b\""."\"c.d\""="Bar"]') == [element]
|
|
||||||
|
|
||||||
def test_comparing_if_value_is_in_a_list(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element.Name = "Foobar"
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name%="Foobar,Foobaz"]') == [element]
|
|
||||||
element.Name = "Foobaz"
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name%="Foobar,Foobaz"]') == [element]
|
|
||||||
element.Name = "Foobat"
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name%="Foobar,Foobaz"]') == []
|
|
||||||
|
|
||||||
def test_getting_occurrences_of_a_filtered_type(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
|
|
||||||
ifcopenshell.api.run("type.assign_type", self.file, related_objects=[element], relating_type=element_type)
|
|
||||||
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element_type2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
|
|
||||||
ifcopenshell.api.run("type.assign_type", self.file, related_objects=[element2], relating_type=element_type2)
|
|
||||||
assert set(subject.Selector.parse(self.file, "* .IfcWallType")) == {element, element2}
|
|
||||||
|
|
||||||
def test_getting_decomposition_of_a_filtered_type(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcMember")
|
|
||||||
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
|
||||||
ifcopenshell.api.run("spatial.assign_container", self.file, products=[element], relating_structure=building)
|
|
||||||
ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element)
|
|
||||||
assert set(subject.Selector.parse(self.file, "@ .IfcBuilding")) == {element, subelement}
|
|
||||||
|
|
||||||
def test_selecting_elements_from_a_prefiltered_list(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSlab")
|
|
||||||
assert subject.Selector.parse(self.file, ".IfcWall", elements=[element])
|
|
||||||
assert not subject.Selector.parse(self.file, ".IfcWall", elements=[element2])
|
|
||||||
|
|
||||||
def test_selecting_a_property_via_a_wildcard_pset_name(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foobar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Bar"})
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foobaz")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Baz"})
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[r"Foo.*"."Foo"="Bar"]') == [element]
|
|
||||||
|
|
||||||
def test_selecting_a_property_via_a_wildcard_property_name(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foobar")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Bar"})
|
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foobaz")
|
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Baz"})
|
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[r"Foo.*"."F.*"="Bar"]') == [element]
|
|
||||||
|
|
||||||
def test_selecting_an_attribute_via_a_type(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
|
|
||||||
element_type.Name = "Foo"
|
|
||||||
ifcopenshell.api.run("type.assign_type", self.file, related_objects=[element], relating_type=element_type)
|
|
||||||
assert set(subject.Selector.parse(self.file, '.IfcWall[type.Name="Foo"]')) == {element}
|
|
||||||
|
|
||||||
def test_selecting_via_a_material(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
material = ifcopenshell.api.run("material.add_material", self.file, name="CON01")
|
|
||||||
ifcopenshell.api.run("material.assign_material", self.file, products=[element], material=material)
|
|
||||||
assert set(subject.Selector.parse(self.file, '.IfcWall[material.Name="CON01"]')) == {element}
|
|
||||||
|
|
||||||
def test_selecting_via_a_material_set(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
material = ifcopenshell.api.run("material.add_material", self.file, name="CON01")
|
|
||||||
material_set = ifcopenshell.api.run(
|
|
||||||
"material.add_material_set", self.file, name="FOO", set_type="IfcMaterialLayerSet"
|
|
||||||
)
|
|
||||||
layer = ifcopenshell.api.run("material.add_layer", self.file, layer_set=material_set, material=material)
|
|
||||||
ifcopenshell.api.run("material.edit_layer", self.file, layer=layer, attributes={"LayerThickness": 13})
|
|
||||||
ifcopenshell.api.run("material.assign_material", self.file, products=[element], material=material_set)
|
|
||||||
assert set(subject.Selector.parse(self.file, '.IfcWall[material.LayerSetName="FOO"]')) == {element}
|
|
||||||
|
|
||||||
def test_selecting_via_a_material_set_item(self):
|
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
|
||||||
material = ifcopenshell.api.run("material.add_material", self.file, name="CON01")
|
|
||||||
material2 = ifcopenshell.api.run("material.add_material", self.file, name="CON02")
|
|
||||||
material_set = ifcopenshell.api.run(
|
|
||||||
"material.add_material_set", self.file, name="FOO", set_type="IfcMaterialLayerSet"
|
|
||||||
)
|
|
||||||
layer = ifcopenshell.api.run("material.add_layer", self.file, layer_set=material_set, material=material)
|
|
||||||
layer = ifcopenshell.api.run("material.add_layer", self.file, layer_set=material_set, material=material2)
|
|
||||||
ifcopenshell.api.run("material.assign_material", self.file, products=[element], material=material_set)
|
|
||||||
assert set(subject.Selector.parse(self.file, '.IfcWall[material.item.Material.Name="CON01"]')) == {element}
|
|
||||||
assert set(subject.Selector.parse(self.file, '.IfcWall[material.item.Material.Name="CON02"]')) == {element}
|
|
||||||
assert set(subject.Selector.parse(self.file, '.IfcWall[material.item.Material.Name="CON03"]')) == set()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user