mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 19:07:57 +00:00
1c26ee86c9
Add --format ids to ifcquery to output step IDs suitable for piping into ifcedit parameters. Add ifcedit foreach to apply an operation to every element in a query result. Extend clash and relations output so --format ids extracts all involved element IDs, enabling one-liners like clash detection piped directly into render. Generated with the assistance of an AI coding tool.
200 lines
7.0 KiB
Python
200 lines
7.0 KiB
Python
# This file was generated with the assistance of an AI coding tool.
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
from ifcquery.relations import relations
|
|
|
|
|
|
class TestWallRelations:
|
|
def test_wall_has_container(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall)
|
|
assert result["id"] == wall.id()
|
|
assert result["type"] == "IfcWall"
|
|
assert result["hierarchy"]["container"]["type"] == "IfcBuildingStorey"
|
|
assert result["hierarchy"]["container"]["name"] == "Ground Floor"
|
|
|
|
def test_wall_has_parent(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall)
|
|
assert result["hierarchy"]["parent"]["type"] == "IfcBuildingStorey"
|
|
|
|
def test_wall_no_children(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall)
|
|
assert "children" not in result
|
|
|
|
def test_wall_empty_categories_omitted(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall)
|
|
assert "groups" not in result
|
|
assert "systems" not in result
|
|
assert "zones" not in result
|
|
assert "connections" not in result
|
|
assert "referenced_structures" not in result
|
|
|
|
|
|
class TestStoreyRelations:
|
|
def test_storey_has_contained(self, model):
|
|
storey = model.by_type("IfcBuildingStorey")[0]
|
|
result = relations(model, storey)
|
|
contained_types = {e["type"] for e in result["children"]["contained"]}
|
|
assert "IfcWall" in contained_types
|
|
assert "IfcSlab" in contained_types
|
|
|
|
def test_storey_has_aggregate_parent(self, model):
|
|
storey = model.by_type("IfcBuildingStorey")[0]
|
|
result = relations(model, storey)
|
|
assert result["hierarchy"]["aggregate"]["type"] == "IfcBuilding"
|
|
assert result["hierarchy"]["aggregate"]["name"] == "TestBuilding"
|
|
|
|
|
|
class TestProjectRelations:
|
|
def test_project_has_parts(self, model):
|
|
project = model.by_type("IfcProject")[0]
|
|
result = relations(model, project)
|
|
parts = result["children"]["parts"]
|
|
assert any(p["type"] == "IfcSite" for p in parts)
|
|
|
|
def test_project_no_hierarchy(self, model):
|
|
project = model.by_type("IfcProject")[0]
|
|
result = relations(model, project)
|
|
assert "hierarchy" not in result
|
|
|
|
|
|
class TestTraverseUp:
|
|
def test_wall_to_project(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
chain = relations(model, wall, traverse="up")
|
|
assert isinstance(chain, list)
|
|
assert chain[0]["type"] == "IfcWall"
|
|
assert chain[-1]["type"] == "IfcProject"
|
|
types = [e["type"] for e in chain]
|
|
assert "IfcBuildingStorey" in types
|
|
assert "IfcBuilding" in types
|
|
assert "IfcSite" in types
|
|
|
|
def test_project_traverse(self, model):
|
|
project = model.by_type("IfcProject")[0]
|
|
chain = relations(model, project, traverse="up")
|
|
assert len(chain) == 1
|
|
assert chain[0]["type"] == "IfcProject"
|
|
|
|
def test_storey_to_project(self, model):
|
|
storey = model.by_type("IfcBuildingStorey")[0]
|
|
chain = relations(model, storey, traverse="up")
|
|
assert chain[0]["type"] == "IfcBuildingStorey"
|
|
assert chain[-1]["type"] == "IfcProject"
|
|
assert len(chain) == 4 # storey -> building -> site -> project
|
|
|
|
|
|
class TestElementsSummary:
|
|
def test_wall_elements_includes_self(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall)
|
|
ids = [e["id"] for e in result["elements"]]
|
|
assert wall.id() in ids
|
|
|
|
def test_wall_elements_includes_container(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall)
|
|
ids = [e["id"] for e in result["elements"]]
|
|
storey = model.by_type("IfcBuildingStorey")[0]
|
|
assert storey.id() in ids
|
|
|
|
def test_storey_elements_includes_contained(self, model):
|
|
storey = model.by_type("IfcBuildingStorey")[0]
|
|
result = relations(model, storey)
|
|
ids = [e["id"] for e in result["elements"]]
|
|
wall = model.by_type("IfcWall")[0]
|
|
assert wall.id() in ids
|
|
|
|
def test_elements_no_duplicates(self, model):
|
|
storey = model.by_type("IfcBuildingStorey")[0]
|
|
result = relations(model, storey)
|
|
ids = [e["id"] for e in result["elements"]]
|
|
assert len(ids) == len(set(ids))
|
|
|
|
def test_elements_all_have_id_and_type(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall)
|
|
for e in result["elements"]:
|
|
assert "id" in e
|
|
assert "type" in e
|
|
|
|
def test_traverse_up_has_no_elements_field(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall, traverse="up")
|
|
assert isinstance(result, list)
|
|
assert not any("elements" in item for item in result)
|
|
|
|
|
|
class TestJsonSerializable:
|
|
def test_relations_serializable(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall)
|
|
json.dumps(result)
|
|
|
|
def test_traverse_serializable(self, model):
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = relations(model, wall, traverse="up")
|
|
json.dumps(result)
|
|
|
|
|
|
class TestCLI:
|
|
@staticmethod
|
|
def _ifc_path(model):
|
|
f = tempfile.NamedTemporaryFile(suffix=".ifc", delete=False)
|
|
model.write(f.name)
|
|
f.close()
|
|
return f.name
|
|
|
|
def test_relations_json(self, model):
|
|
path = self._ifc_path(model)
|
|
try:
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "ifcquery", path, "relations", str(wall.id())],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
assert result.returncode == 0
|
|
data = json.loads(result.stdout)
|
|
assert data["type"] == "IfcWall"
|
|
assert "hierarchy" in data
|
|
finally:
|
|
os.unlink(path)
|
|
|
|
def test_relations_traverse_up(self, model):
|
|
path = self._ifc_path(model)
|
|
try:
|
|
wall = model.by_type("IfcWall")[0]
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "ifcquery", path, "relations", str(wall.id()), "--traverse", "up"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
assert result.returncode == 0
|
|
data = json.loads(result.stdout)
|
|
assert isinstance(data, list)
|
|
assert data[0]["type"] == "IfcWall"
|
|
assert data[-1]["type"] == "IfcProject"
|
|
finally:
|
|
os.unlink(path)
|
|
|
|
def test_relations_bad_id(self, model):
|
|
path = self._ifc_path(model)
|
|
try:
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "ifcquery", path, "relations", "999999"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
assert result.returncode != 0
|
|
assert "Error" in result.stderr
|
|
finally:
|
|
os.unlink(path)
|