IFCore Deploy
deploy(prod): 2026-02-21T01:10:43Z
51982d6
"""Gradio UI for running wall compliance checks on uploaded IFC files."""
import argparse
import sys
from pathlib import Path
import gradio as gr
ROOT_DIR = Path(__file__).resolve().parent.parent
WALLS_DIR = ROOT_DIR / "walls_check"
if str(WALLS_DIR) not in sys.path:
sys.path.insert(0, str(WALLS_DIR))
from Walls import run_wall_checks
import rules
def analyze_ifc_walls(
ifc_file,
min_mm,
min_height_mm,
min_service_height_mm,
use_space_aware_height,
max_u,
climate_zone,
include_summary,
):
if ifc_file is None:
return "", "Please upload an IFC file first."
ifc_path = ifc_file if isinstance(ifc_file, str) else ifc_file.name
normalized_zone = None if climate_zone == "Custom (use max U-value)" else climate_zone
try:
report_lines = run_wall_checks(
ifc_path,
min_mm=float(min_mm),
min_height_mm=float(min_height_mm),
min_service_height_mm=float(min_service_height_mm),
use_space_aware_height=bool(use_space_aware_height),
max_u=float(max_u),
climate_zone=normalized_zone,
include_summary=bool(include_summary),
)
report_text = "\n".join(report_lines)
fail_count = sum(1 for line in report_lines if line.startswith("[FAIL] IfcWall "))
if normalized_zone:
u_info = f"U-limit mode: climate zone {normalized_zone} (U_lim={rules.CLIMATE_ZONE_U_LIMITS[normalized_zone]:.2f})"
else:
u_info = f"U-limit mode: custom max U={float(max_u):.3f}"
if use_space_aware_height:
h_info = f"Height mode: room-aware ({float(min_height_mm):.0f}/{float(min_service_height_mm):.0f}mm)"
else:
h_info = f"Height mode: fixed ({float(min_height_mm):.0f}mm)"
status = f"Analyzed: {Path(ifc_path).name} | Failed checks: {fail_count} | {u_info} | {h_info}"
return report_text, status
except Exception as exc:
return "", f"Error analyzing {Path(ifc_path).name}: {exc}"
def build_app():
with gr.Blocks(title="IFC Wall Compliance Checker") as app:
gr.Markdown(
"""
# IFC Wall Compliance Checker
Upload an IFC model and run wall compliance checks.
`U-value` (thermal transmittance) is in W/m2K: lower is better insulation.
If room-aware height is enabled: 2.50m general, 2.20m for kitchen/bath/corridor (based on IfcSpace links).
"""
)
with gr.Row():
with gr.Column(scale=1):
ifc_input = gr.File(
label="Upload IFC file",
file_types=[".ifc"],
type="filepath",
)
min_mm = gr.Number(label="Minimum wall thickness (mm)", value=100, precision=0)
min_height_mm = gr.Number(label="General minimum wall height (mm)", value=2500, precision=0)
min_service_height_mm = gr.Number(
label="Service-space minimum height (mm)",
value=2200,
precision=0,
)
use_space_aware_height = gr.Checkbox(
label="Use room-aware height (kitchen/bath/corridor)",
value=True,
)
max_u = gr.Number(label="Maximum U-value", value=0.8, precision=3)
climate_zone = gr.Dropdown(
label="Climate zone (CTE external-wall U limit)",
choices=["Custom (use max U-value)", "A", "B", "C", "D", "E"],
value="Custom (use max U-value)",
)
include_summary = gr.Checkbox(label="Include summary section", value=True)
analyze_btn = gr.Button("Run wall checks", variant="primary")
clear_btn = gr.Button("Clear")
with gr.Column(scale=2):
status = gr.Markdown("Upload a file and click **Run wall checks**.")
output = gr.Textbox(
label="Compliance report",
lines=24,
max_lines=40,
)
analyze_btn.click(
fn=analyze_ifc_walls,
inputs=[
ifc_input,
min_mm,
min_height_mm,
min_service_height_mm,
use_space_aware_height,
max_u,
climate_zone,
include_summary,
],
outputs=[output, status],
)
clear_btn.click(
fn=lambda: ("", "Upload a file and click **Run wall checks**."),
inputs=[],
outputs=[output, status],
)
return app
def parse_args():
parser = argparse.ArgumentParser(description="Run the wall compliance Gradio app.")
parser.add_argument("--host", default="127.0.0.1", help="Host interface for Gradio server.")
parser.add_argument("--port", type=int, default=7860, help="Port for Gradio server.")
parser.add_argument(
"--share",
action="store_true",
help="Create a temporary public Gradio link.",
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
build_app().launch(server_name=args.host, server_port=args.port, share=args.share)