From a6bff09c7897ce0745ca3c8eb3bd5fa4f5ba8b1a Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 1 Sep 2023 16:47:55 +1000 Subject: [PATCH] Update BBIM tester module UI to be compatible with new IfcTester upgrades --- .../blenderbim/bim/module/tester/data.py | 45 +++++++++++++++++++ .../blenderbim/bim/module/tester/operator.py | 43 +++++++++--------- .../blenderbim/bim/module/tester/prop.py | 10 ++--- .../blenderbim/bim/module/tester/ui.py | 42 +++++++---------- src/blenderbim/blenderbim/tool/__init__.py | 1 + 5 files changed, 89 insertions(+), 52 deletions(-) create mode 100644 src/blenderbim/blenderbim/bim/module/tester/data.py diff --git a/src/blenderbim/blenderbim/bim/module/tester/data.py b/src/blenderbim/blenderbim/bim/module/tester/data.py new file mode 100644 index 0000000000..742b471f89 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/tester/data.py @@ -0,0 +1,45 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2023 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import bpy +import blenderbim.tool as tool + + +def refresh(): + TesterData.is_loaded = False + + +class TesterData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"has_report": cls.has_report(), "specification": cls.specification()} + cls.is_loaded = True + + @classmethod + def has_report(cls): + return tool.Tester.report + + @classmethod + def specification(cls): + if not tool.Tester.report: + return {} + props = bpy.context.scene.IfcTesterProperties + return tool.Tester.report[props.active_specification_index] diff --git a/src/blenderbim/blenderbim/bim/module/tester/operator.py b/src/blenderbim/blenderbim/bim/module/tester/operator.py index 0164f0a11d..71150627dc 100644 --- a/src/blenderbim/blenderbim/bim/module/tester/operator.py +++ b/src/blenderbim/blenderbim/bim/module/tester/operator.py @@ -21,15 +21,16 @@ import bpy import time import tempfile import webbrowser -import json import ifctester import ifctester.ids import ifctester.reporter import ifcopenshell import blenderbim.tool as tool +import blenderbim.bim.handler +from blenderbim.bim.module.tester.data import TesterData -class ExecuteIfcTester(bpy.types.Operator): +class ExecuteIfcTester(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.execute_ifc_tester" bl_label = "Execute IfcTester" @@ -54,28 +55,27 @@ class ExecuteIfcTester(bpy.types.Operator): print("Finished loading:", time.time() - start) start = time.time() specs.validate(ifc) - + print("Finished validating:", time.time() - start) start = time.time() if props.generate_html_report: engine = ifctester.reporter.Html(specs) - engine.report() + engine.report() engine.to_file(output) webbrowser.open("file://" + output) - + report = None - report = ifctester.reporter.Json(specs).report()['specifications'] + report = ifctester.reporter.Json(specs).report()["specifications"] if report: - props.has_report = True - props.report = json.dumps(report) + tool.Tester.report = report props.specifications.clear() - c=0 for spec in report: new_spec = props.specifications.add() - new_spec.name = spec['name'] - new_spec.status = spec['status'] + new_spec.name = spec["name"] + new_spec.status = spec["status"] + blenderbim.bim.handler.refresh_ui_data() return {"FINISHED"} @@ -118,23 +118,24 @@ class SelectRequirement(bpy.types.Operator): bl_label = "Select Specification" bl_options = {"REGISTER", "UNDO"} spec_index: bpy.props.IntProperty() - req_index: bpy.props.IntProperty() + req_index: bpy.props.IntProperty() def execute(self, context): props = context.scene.IfcTesterProperties - report = json.loads(props.report) + report = tool.Tester.report props.old_index = self.spec_index - failed_entities = report[self.spec_index]['requirements'] [self.req_index]['failed_entities'] + failed_entities = report[self.spec_index]["requirements"][self.req_index]["failed_entities"] props.n_entities = len(failed_entities) props.has_entities = True if props.n_entities > 0 else False props.failed_entities.clear() for e in failed_entities: - new_entity = props.failed_entities.add() - new_entity.element = e['element'] - new_entity.reason = e['reason'] + new_entity = props.failed_entities.add() + new_entity.element = e["element"] + new_entity.reason = e["reason"] return {"FINISHED"} + class SelectEntity(bpy.types.Operator): bl_idname = "bim.select_entity" bl_label = "Select Entity" @@ -142,13 +143,14 @@ class SelectEntity(bpy.types.Operator): ifc_id: bpy.props.IntProperty() def execute(self, context): - bpy.ops.object.select_all(action='DESELECT') + bpy.ops.object.select_all(action="DESELECT") for obj in context.scene.objects: if obj.BIMObjectProperties.ifc_definition_id == self.ifc_id: obj.select_set(True) bpy.context.view_layer.objects.active = obj return {"FINISHED"} - + + class ExportBcf(bpy.types.Operator): bl_idname = "bim.export_bcf" bl_label = "Export BCF" @@ -168,7 +170,6 @@ class ExportBcf(bpy.types.Operator): bcf_reporter.report() bcf_reporter.to_file(output) print("Finished exporting!") - self.report({"INFO"}, 'Finished exporting!') + self.report({"INFO"}, "Finished exporting!") return {"FINISHED"} - diff --git a/src/blenderbim/blenderbim/bim/module/tester/prop.py b/src/blenderbim/blenderbim/bim/module/tester/prop.py index 63637301a3..b68600f053 100644 --- a/src/blenderbim/blenderbim/bim/module/tester/prop.py +++ b/src/blenderbim/blenderbim/bim/module/tester/prop.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from blenderbim.bim.module.tester.data import TesterData from blenderbim.bim.prop import StrProperty from bpy.types import PropertyGroup from bpy.props import ( @@ -34,8 +35,8 @@ def purge(): pass -def get_failure_entities(): - return +def update_active_specification_index(self, context): + TesterData.load() class Specification(PropertyGroup): @@ -53,13 +54,10 @@ class IfcTesterProperties(PropertyGroup): ifc_file: StringProperty(default="", name="IFC File") should_load_from_memory: BoolProperty(default=False, name="Load from Memory") generate_html_report: BoolProperty(default=False, name="Generate HTML report") - active_specification_index: IntProperty(name="Active Specification Index") - active_requirement_index: IntProperty(name="Active Requirement Index") + active_specification_index: IntProperty(name="Active Specification Index", update=update_active_specification_index) old_index: IntProperty(name="", default=0) active_failed_entity_index: IntProperty(name="Active Failed Entity Index") - report: StringProperty(default="", name="JSON report") specifications: CollectionProperty(name="Specifications", type=Specification) failed_entities: CollectionProperty(name="FailedEntities", type=FailedEntities) - has_report: BoolProperty(default=False, name="") has_entities: BoolProperty(default=False, name="") n_entities: IntProperty(name="", default=0) diff --git a/src/blenderbim/blenderbim/bim/module/tester/ui.py b/src/blenderbim/blenderbim/bim/module/tester/ui.py index 3db173884e..2af3f7af98 100644 --- a/src/blenderbim/blenderbim/bim/module/tester/ui.py +++ b/src/blenderbim/blenderbim/bim/module/tester/ui.py @@ -18,7 +18,7 @@ import blenderbim.tool as tool from bpy.types import Panel, UIList -import json +from blenderbim.bim.module.tester.data import TesterData class BIM_PT_tester(Panel): @@ -31,6 +31,9 @@ class BIM_PT_tester(Panel): bl_parent_id = "BIM_PT_tab_quality_control" def draw(self, context): + if not TesterData.is_loaded: + TesterData.load() + self.layout.use_property_split = True props = context.scene.IfcTesterProperties @@ -53,7 +56,7 @@ class BIM_PT_tester(Panel): row = self.layout.row() row.operator("bim.execute_ifc_tester") - if props.has_report: + if TesterData.data["has_report"]: self.layout.template_list( "BIM_UL_tester_specifications", "", @@ -69,33 +72,26 @@ class BIM_PT_tester(Panel): def draw_editable_ui(self, context): props = context.scene.IfcTesterProperties - i = props.active_specification_index - dic_report = json.loads(props.report) + specification = TesterData.data["specification"] - total_successes = dic_report[i]["total_successes"] - total = dic_report[i]["total"] - percentage = dic_report[i]["percentage"] - n_requirements = len(dic_report[i]["requirements"]) + n_requirements = len(specification["requirements"]) row = self.layout.row() - row.label(text=f"Passed: {total_successes}/{total} ({percentage}%)") + row.label( + text=f'Passed: {specification["total_checks_pass"]}/{specification["total_checks"]} ({specification["percent_checks_pass"]}%)' + ) row = self.layout.row() row.label(text=f"Requirements ({n_requirements}):") - c = 0 box = self.layout.box() - for req in dic_report[i]["requirements"]: + for i, requirement in enumerate(specification["requirements"]): row = box.row(align=True) - row.label(text=f" {c+1}. {req['description']}") - if req["status"]: - row.label(text="PASS", icon="CHECKMARK") - else: - row.label(text="FAIL", icon="CANCEL") + row.label(text=requirement["description"], icon="CHECKMARK" if requirement["status"] else "CANCEL") + if not requirement["status"]: op = row.operator("bim.select_requirement", text="", icon="LONGDISPLAY") - op.spec_index = i - op.req_index = c - c += 1 + op.spec_index = props.active_specification_index + op.req_index = i - if props.old_index == i and props.n_entities > 0: + if props.old_index == props.active_specification_index and props.n_entities > 0: row = self.layout.row() row.label(text=f"Failed entities [{props.n_entities}]:") self.layout.template_list( @@ -112,11 +108,7 @@ class BIM_UL_tester_specifications(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: row = layout.row(align=True) - row.label(text=item.name, icon="WORDWRAP_ON") - if item.status: - row.label(text="PASS") - else: - row.label(text="FAIL") + row.label(text=item.name, icon="CHECKMARK" if item.status else "CANCEL") class BIM_UL_tester_failed_entities(UIList): diff --git a/src/blenderbim/blenderbim/tool/__init__.py b/src/blenderbim/blenderbim/tool/__init__.py index 118f61a047..54f5e9ea57 100644 --- a/src/blenderbim/blenderbim/tool/__init__.py +++ b/src/blenderbim/blenderbim/tool/__init__.py @@ -51,6 +51,7 @@ from blenderbim.tool.structural import Structural from blenderbim.tool.style import Style from blenderbim.tool.surveyor import Surveyor from blenderbim.tool.system import System +from blenderbim.tool.tester import Tester from blenderbim.tool.type import Type from blenderbim.tool.unit import Unit from blenderbim.tool.search import Search