mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 02:47:48 +00:00
7cd40bf8cb
ifcquery is a new command-line tool for querying and inspecting IFC models. All output is JSON.
Subcommands:
summary — schema version, entity counts, project metadata
tree — full spatial hierarchy (Project → Site → Building → Storeys → Spaces → Elements)
info <id> — deep inspection of any entity by step ID (attributes, psets, placement matrix, type, material)
select <query> — filter elements using ifcopenshell selector syntax
relations <id> — relationships for an element; --traverse up walks to IfcProject
clash <id> — geometric intersection and clearance detection
validate — schema/constraint validation; --rules adds EXPRESS checks
schedule — work schedules with nested task trees
cost — cost schedules with nested cost item trees
schema <class> — IFC class documentation from the model's schema version
plot — SVG plan drawing
render — 3D geometry rendering
contexts — geometric representation contexts
materials — material assignments
Usage:
python3 -m ifcquery <file.ifc> <subcommand> [args]
Generated with the assistance of an AI coding tool.
122 lines
4.9 KiB
Python
122 lines
4.9 KiB
Python
# This file was generated with the assistance of an AI coding tool.
|
|
from __future__ import annotations
|
|
|
|
import ifcopenshell
|
|
import ifcopenshell.api.aggregate
|
|
import ifcopenshell.api.owner.settings
|
|
import ifcopenshell.api.project
|
|
import ifcopenshell.api.root
|
|
import ifcopenshell.api.sequence
|
|
import ifcopenshell.api.unit
|
|
import pytest
|
|
|
|
from ifcquery.schedule import schedule
|
|
|
|
|
|
@pytest.fixture
|
|
def schedule_model():
|
|
"""Create an IFC4 model with a work schedule and nested tasks."""
|
|
f = ifcopenshell.api.project.create_file()
|
|
ifcopenshell.api.owner.settings.get_user = lambda ifc: (ifc.by_type("IfcPersonAndOrganization") or [None])[0]
|
|
ifcopenshell.api.owner.settings.get_application = lambda ifc: (ifc.by_type("IfcApplication") or [None])[0]
|
|
|
|
project = ifcopenshell.api.root.create_entity(f, ifc_class="IfcProject", name="TestProject")
|
|
ifcopenshell.api.unit.assign_unit(f)
|
|
|
|
ws = ifcopenshell.api.sequence.add_work_schedule(f, name="Construction Schedule")
|
|
|
|
task1 = ifcopenshell.api.sequence.add_task(f, work_schedule=ws, name="Phase 1", identification="P1")
|
|
tt1 = ifcopenshell.api.sequence.add_task_time(f, task=task1)
|
|
ifcopenshell.api.sequence.edit_task_time(
|
|
f, task_time=tt1, attributes={"ScheduleStart": "2024-01-01", "ScheduleFinish": "2024-06-30"}
|
|
)
|
|
|
|
task2 = ifcopenshell.api.sequence.add_task(f, work_schedule=ws, name="Phase 2", identification="P2")
|
|
subtask = ifcopenshell.api.sequence.add_task(f, parent_task=task1, name="Sub Task", identification="S1")
|
|
|
|
return f
|
|
|
|
|
|
class TestSchedule:
|
|
def test_returns_list(self, schedule_model):
|
|
result = schedule(schedule_model)
|
|
assert isinstance(result, list)
|
|
|
|
def test_finds_work_schedule(self, schedule_model):
|
|
result = schedule(schedule_model)
|
|
assert len(result) == 1
|
|
|
|
def test_work_schedule_has_name(self, schedule_model):
|
|
result = schedule(schedule_model)
|
|
assert result[0]["name"] == "Construction Schedule"
|
|
|
|
def test_work_schedule_has_id(self, schedule_model):
|
|
result = schedule(schedule_model)
|
|
assert isinstance(result[0]["id"], int)
|
|
assert result[0]["id"] > 0
|
|
|
|
def test_work_schedule_has_tasks(self, schedule_model):
|
|
result = schedule(schedule_model)
|
|
tasks = result[0]["tasks"]
|
|
assert isinstance(tasks, list)
|
|
assert len(tasks) >= 1
|
|
|
|
def test_task_has_required_fields(self, schedule_model):
|
|
result = schedule(schedule_model)
|
|
task = result[0]["tasks"][0]
|
|
assert "id" in task
|
|
assert "name" in task
|
|
assert "start" in task
|
|
assert "finish" in task
|
|
assert "is_milestone" in task
|
|
assert "outputs" in task
|
|
assert "subtasks" in task
|
|
|
|
def test_task_name(self, schedule_model):
|
|
result = schedule(schedule_model)
|
|
task_names = [t["name"] for t in result[0]["tasks"]]
|
|
assert "Phase 1" in task_names
|
|
|
|
def test_task_start_finish(self, schedule_model):
|
|
result = schedule(schedule_model)
|
|
phase1 = next(t for t in result[0]["tasks"] if t["name"] == "Phase 1")
|
|
assert phase1["start"] is not None
|
|
assert phase1["finish"] is not None
|
|
|
|
def test_subtasks(self, schedule_model):
|
|
result = schedule(schedule_model)
|
|
phase1 = next(t for t in result[0]["tasks"] if t["name"] == "Phase 1")
|
|
assert len(phase1["subtasks"]) == 1
|
|
assert phase1["subtasks"][0]["name"] == "Sub Task"
|
|
|
|
def test_empty_model_returns_empty_list(self, model):
|
|
result = schedule(model)
|
|
assert result == []
|
|
|
|
def test_max_depth_none_returns_full_tree(self, schedule_model):
|
|
result = schedule(schedule_model, max_depth=None)
|
|
phase1 = next(t for t in result[0]["tasks"] if t["name"] == "Phase 1")
|
|
assert isinstance(phase1["subtasks"], list)
|
|
assert len(phase1["subtasks"]) == 1
|
|
|
|
def test_max_depth_1_truncates_subtasks(self, schedule_model):
|
|
result = schedule(schedule_model, max_depth=1)
|
|
phase1 = next(t for t in result[0]["tasks"] if t["name"] == "Phase 1")
|
|
assert isinstance(phase1["subtasks"], dict)
|
|
assert phase1["subtasks"]["truncated"] is True
|
|
assert phase1["subtasks"]["count"] == 1
|
|
|
|
def test_max_depth_truncation_shows_count(self, schedule_model):
|
|
result = schedule(schedule_model, max_depth=1)
|
|
# Phase 2 has no subtasks — should return empty list, not truncation dict
|
|
phase2 = next(t for t in result[0]["tasks"] if t["name"] == "Phase 2")
|
|
assert phase2["subtasks"] == []
|
|
|
|
def test_max_depth_2_expands_to_depth_2(self, schedule_model):
|
|
result = schedule(schedule_model, max_depth=2)
|
|
phase1 = next(t for t in result[0]["tasks"] if t["name"] == "Phase 1")
|
|
# subtask at depth 2 should be fully expanded (it has no children)
|
|
assert isinstance(phase1["subtasks"], list)
|
|
assert phase1["subtasks"][0]["name"] == "Sub Task"
|
|
assert phase1["subtasks"][0]["subtasks"] == []
|