From f7a7626575db4ac93dabe42e5593f32d0e48d685 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 8 Nov 2024 14:48:43 +0500 Subject: [PATCH] ifctester to fail more gracefully meeting invalid .ids file Previously Bonsai would show a wall of errors, now it's show a simple error message and redirecting to system console for the details. Example error - https://i.imgur.com/uJJfjUW.png Example validation error details in console - https://i.imgur.com/3mcVBUh.png Same details but in text: Validation error details: failed validating {'dataType': 'IFCBOOLEAN', 'ursi': 'https://google.com', 'cardinality': 'required', 'instructions': "Make sure it's true"} with XsdAttributeGroup(['dataType', 'uri', 'cardinality', 'instructions']): Reason: 'ursi' attribute not allowed for element Schema component: Author of the IDS can leave instructions for the authors of the IFC. This text could/should be displayed in the BIM/IFC authoring tool. Instance type: Instance: Pset_WallCommon Combustible false Path: /ids:ids/ids:specifications/ids:specification/ids:requirements/ids:property[1] --- .../bonsai/bim/module/tester/operator.py | 15 ++++++++++++- src/ifctester/ifctester/ids.py | 21 ++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/tester/operator.py b/src/bonsai/bonsai/bim/module/tester/operator.py index cd466bc377..521dd2ed04 100644 --- a/src/bonsai/bonsai/bim/module/tester/operator.py +++ b/src/bonsai/bonsai/bim/module/tester/operator.py @@ -21,6 +21,7 @@ import bpy import time import tempfile import webbrowser +import traceback import ifctester import ifctester.ids import ifctester.reporter @@ -72,7 +73,19 @@ class ExecuteIfcTester(bpy.types.Operator, tool.Ifc.Operator): start = time.time() output = Path(os.path.join(dirpath, "{}_{}.html".format(ifc_path, os.path.basename(specs_path)))) - specs = ifctester.ids.open(specs_path) + try: + specs = ifctester.ids.open(specs_path) + except ifctester.ids.IdsXmlValidationError as e: + traceback.print_exc() + YELLOW = "\033[93m" + RESET = "\033[0m" + print("------------------\n" * 3) + print(f"{YELLOW}Validation error details:\n\n{str(e.xml_error)}{RESET}") + print("------------------\n" * 3) + self.report( + {"ERROR"}, "Provided IDS file appears to be invalid. Open system console to see the details." + ) + return {"CANCELLED"} print("Finished loading:", time.time() - start) start = time.time() specs.validate(ifc_data, filepath=ifc_path) diff --git a/src/ifctester/ifctester/ids.py b/src/ifctester/ifctester/ids.py index 7242c2f812..2de95a6d4e 100644 --- a/src/ifctester/ifctester/ids.py +++ b/src/ifctester/ifctester/ids.py @@ -20,6 +20,7 @@ from __future__ import annotations import os import datetime import ifcopenshell +from xmlschema.validators.exceptions import XMLSchemaValidationError from xmlschema import XMLSchema from xmlschema import etree_tostring from xml.etree import ElementTree as ET @@ -43,16 +44,26 @@ cwd = os.path.dirname(os.path.realpath(__file__)) schema = None +class IdsXmlValidationError(Exception): + def __init__(self, xml_error: XMLSchemaValidationError, message: str): + self.xml_error = xml_error + super().__init__(message) + + @overload def open(filepath: str, validate: Literal[False] = False) -> Ids: ... @overload def open(filepath: str, validate: Literal[True]) -> None: ... def open(filepath: str, validate=False) -> Union[Ids, None]: - if validate: - get_schema().validate(filepath) - return Ids().parse( - get_schema().decode(filepath, strip_namespaces=True, namespaces={"": "http://standards.buildingsmart.org/IDS"}) - ) + try: + if validate: + get_schema().validate(filepath) + decode = get_schema().decode( + filepath, strip_namespaces=True, namespaces={"": "http://standards.buildingsmart.org/IDS"} + ) + except XMLSchemaValidationError as e: + raise IdsXmlValidationError(e, f"Provided .ids file ({filepath}) appears to be invalid. See details above.") + return Ids().parse(decode) def get_schema():