diff --git a/src/ifcbimtester/bimtester/features/environment.py b/src/ifcbimtester/bimtester/features/environment.py index 1089fef7e4..25f6d040ca 100644 --- a/src/ifcbimtester/bimtester/features/environment.py +++ b/src/ifcbimtester/bimtester/features/environment.py @@ -18,56 +18,37 @@ def before_all(context): if context.config.lang: switch_locale(userdata.get("localedir"), context.config.lang) - context.ifc_path = userdata["ifc"] - context.ifc_basename = os.path.basename( - os.path.splitext(context.ifc_path)[0] - ) - - continue_after_failed = userdata.getbool( - "runner.continue_after_failed_step", True - ) + continue_after_failed = userdata.getbool("runner.continue_after_failed_step", True) Scenario.continue_after_failed_step = continue_after_failed - # keep out path - context.outpath = os.path.join(this_path, "..") + # TODO: refactor smart view support into a decoupled module + # context.ifc_path = userdata.get("ifc", "") + # context.ifc_basename = os.path.basename( + # os.path.splitext(context.ifc_path)[0] + # ) - # since bimtesterfc directory in tmp is removed on every run - # neither log file nor sm file does need to be explicit removed first + # context.outpath = os.path.join(this_path, "..") - # set up log file - context.thelogfile = os.path.join( - context.outpath, - context.ifc_basename + ".log" - ) - create_logfile( - context.thelogfile, - context.ifc_basename, - ) + # context.thelogfile = os.path.join(context.outpath, context.ifc_basename + ".log") + # create_logfile( + # context.thelogfile, + # context.ifc_basename, + # ) - # set up smart view file - context.smview_file = os.path.join( - context.outpath, - context.ifc_basename + ".bcsv" - ) - create_zoom_smartview( - context.smview_file, - context.ifc_basename, - ) + # # set up smart view file + # context.smview_file = os.path.join(context.outpath, context.ifc_basename + ".bcsv") + # create_zoom_smartview( + # context.smview_file, + # context.ifc_basename, + # ) def after_step(context, step): - - if step.status == "failed": - - # append log file - append_logfile(context, step) - - # extend smart view - if hasattr(context, "falseguids"): - # print(context.falseguids) - - append_zoom_smartview( - context.smview_file, - step.name, - context.falseguids - ) + pass + # TODO: refactor smart view support into a decoupled module + #if step.status == "failed": + # # append log file + # append_logfile(context, step) + # # extend smart view + # if hasattr(context, "falseguids"): + # append_zoom_smartview(context.smview_file, step.name, context.falseguids) diff --git a/src/ifcblenderexport/blenderbim/bim/module/bimtester/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/bimtester/__init__.py index a42cce8b97..2a1c4bf008 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/bimtester/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/module/bimtester/__init__.py @@ -4,7 +4,8 @@ from . import ui, prop, operator classes = ( operator.ExecuteBIMTester, operator.BIMTesterPurge, - operator.SelectFeaturesDir, + operator.SelectFeature, + operator.SelectBIMTesterIfcFile, operator.RejectElement, operator.ApproveClass, operator.RejectClass, diff --git a/src/ifcblenderexport/blenderbim/bim/module/bimtester/operator.py b/src/ifcblenderexport/blenderbim/bim/module/bimtester/operator.py index d3c4df2b4d..0ec0a4cf4f 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/bimtester/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/bimtester/operator.py @@ -1,10 +1,11 @@ +import os import bpy +import tempfile +import webbrowser import ifcopenshell import bimtester import bimtester.run import bimtester.reports -import os -import webbrowser from pathlib import Path from itertools import cycle from blenderbim.bim.ifc import IfcStore @@ -15,33 +16,26 @@ class ExecuteBIMTester(bpy.types.Operator): bl_label = "Execute BIMTester" def execute(self, context): + props = context.scene.BimTesterProperties - filename = os.path.join( - bpy.context.scene.BimTesterProperties.features_dir, - bpy.context.scene.BimTesterProperties.features_file + ".feature", - ) - cwd = os.getcwd() - os.chdir(bpy.context.scene.BimTesterProperties.features_dir) - bimtester.run.run_tests({ - "advanced_arguments": "", - "console": False, - "featuresdir": "", - "feature": filename, - "gui": False, - "ifcfile": "", - "purge": False, - "path": "", - }) - bimtester.reports.generate_report() - webbrowser.open( - "file://" - + os.path.join( - bpy.context.scene.BimTesterProperties.features_dir, - "report", - bpy.context.scene.BimTesterProperties.features_file + ".feature.html", - ) - ) - os.chdir(cwd) + with tempfile.TemporaryDirectory() as dirpath: + report = os.path.join(dirpath, "{}.html".format(props.feature)) + args = { + "action": "run", + "advanced_arguments": "", + "console": False, + "feature": props.feature, + "ifc": props.ifc_file, + "path": "", + "report": report, + "schema": "", + "lang": "en" + } + use_stored_ifc = props.should_load_from_memory and IfcStore.get_file() + runner = bimtester.run.TestRunner(args["ifc"], ifc=IfcStore.get_file() if use_stored_ifc else None) + report_json = runner.run(args) + bimtester.reports.ReportGenerator().generate(report_json, args["report"]) + webbrowser.open("file://" + report) return {"FINISHED"} @@ -50,7 +44,6 @@ class BIMTesterPurge(bpy.types.Operator): bl_label = "Purge Tests" def execute(self, context): - filename = os.path.join( bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature", @@ -62,15 +55,31 @@ class BIMTesterPurge(bpy.types.Operator): return {"FINISHED"} -class SelectFeaturesDir(bpy.types.Operator): - bl_idname = "bim.select_features_dir" - bl_label = "Select Features Directory" +class SelectFeature(bpy.types.Operator): + bl_idname = "bim.select_feature" + bl_label = "Select Feature" + filename_ext = ".feature" + filter_glob: bpy.props.StringProperty(default="*.feature", options={"HIDDEN"}) filepath: bpy.props.StringProperty(subtype="FILE_PATH") def execute(self, context): - bpy.context.scene.BimTesterProperties.features_dir = ( - os.path.dirname(os.path.abspath(self.filepath)) if "." in self.filepath else self.filepath - ) + bpy.context.scene.BimTesterProperties.feature = self.filepath + return {"FINISHED"} + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {"RUNNING_MODAL"} + + +class SelectBIMTesterIfcFile(bpy.types.Operator): + bl_idname = "bim.select_bimtester_ifc_file" + bl_label = "Select BIMTester IFC File" + filename_ext = ".ifc" + filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"}) + filepath: bpy.props.StringProperty(subtype="FILE_PATH") + + def execute(self, context): + context.scene.BimTesterProperties.ifc_file = self.filepath return {"FINISHED"} def invoke(self, context, event): diff --git a/src/ifcblenderexport/blenderbim/bim/module/bimtester/prop.py b/src/ifcblenderexport/blenderbim/bim/module/bimtester/prop.py index ca460e1bdd..95a53a4578 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/bimtester/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/module/bimtester/prop.py @@ -2,6 +2,7 @@ import os from pathlib import Path from blenderbim.bim.ifc import IfcStore from blenderbim.bim.prop import StrProperty +from blenderbim.bim.module.root.prop import getIfcClasses from bpy.types import PropertyGroup from bpy.props import ( PointerProperty, @@ -15,53 +16,16 @@ from bpy.props import ( ) scenarios_enum = [] -featuresfiles_enum = [] classes_enum = [] -def getIfcClasses(self, context): # This is a copy of the one in bim.prop (as it is used in other modules, can be refactored later) - global classes_enum - file = IfcStore.get_file() - if len(classes_enum) < 1 and file: - declaration = IfcStore.get_schema().declaration_by_name(self.ifc_product) - def get_classes(declaration): - results = [] - if not declaration.is_abstract(): - results.append(declaration.name()) - for subtype in declaration.subtypes(): - results.extend(get_classes(subtype)) - return results - classes = get_classes(declaration) - classes_enum.extend([(c, c, "") for c in sorted(classes)]) - return classes_enum - - -def getFeaturesFiles(self, context): - global featuresfiles_enum - if len(featuresfiles_enum) < 1: - featuresfiles_enum.clear() - for filename in Path(context.scene.BimTesterProperties.features_dir).glob("*.feature"): - f = str(filename.stem) - featuresfiles_enum.append((f, f, "")) - return featuresfiles_enum - - -def refreshFeaturesFiles(self, context): - global featuresfiles_enum - featuresfiles_enum.clear() - getFeaturesFiles(self, context) - - def getScenarios(self, context): global scenarios_enum if len(scenarios_enum) < 1: scenarios_enum.clear() - if context.scene.BimTesterProperties.features_file != '': # To handle the error when no .feature file exists in the folder - filename = os.path.join( - context.scene.BimTesterProperties.features_dir, context.scene.BimTesterProperties.features_file + ".feature" - ) - with open(filename, "r") as feature_file: + if context.scene.BimTesterProperties.feature != "": + with open(context.scene.BimTesterProperties.feature, "r") as feature_file: lines = feature_file.readlines() for line in lines: if "Scenario:" in line: @@ -77,13 +41,9 @@ def refreshScenarios(self, context): class BimTesterProperties(PropertyGroup): - features_dir: StringProperty(default="", name="Features Directory", update=refreshFeaturesFiles) - features_file: EnumProperty(items=getFeaturesFiles, name="Features File", update=refreshScenarios) + feature: StringProperty(default="", name="Feature File", update=refreshScenarios) + ifc_file: StringProperty(default="", name="IFC File") audit_ifc_class: EnumProperty(items=getIfcClasses, name="Audit Class") qa_reject_element_reason: StringProperty(name="Element Rejection Reason") scenario: EnumProperty(items=getScenarios, name="Scenario") - # should_load_from_memory: BoolProperty(default=False, name="Load from Memory") # can be added later to mimic the functionality in the CSV Module - - - - + should_load_from_memory: BoolProperty(default=False, name="Load from Memory") diff --git a/src/ifcblenderexport/blenderbim/bim/module/bimtester/ui.py b/src/ifcblenderexport/blenderbim/bim/module/bimtester/ui.py index 7026417f16..b660fbcb7f 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/bimtester/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/module/bimtester/ui.py @@ -1,5 +1,5 @@ -import bpy from bpy.types import Panel +from blenderbim.bim.ifc import IfcStore class BIM_PT_qa(Panel): @@ -11,47 +11,48 @@ class BIM_PT_qa(Panel): bl_context = "scene" def draw(self, context): - layout = self.layout - layout.use_property_split = True + self.layout.use_property_split = True - scene = context.scene - bimtester_properties = bpy.context.scene.BimTesterProperties + props = context.scene.BimTesterProperties - layout.label(text="Gherkin Setup:") + if IfcStore.get_file(): + row = self.layout.row() + row.prop(props, "should_load_from_memory") - row = layout.row(align=True) - row.prop(bimtester_properties, "features_dir") - row.operator("bim.select_features_dir", icon="FILE_FOLDER", text="") + if not IfcStore.get_file() or not props.should_load_from_memory: + row = self.layout.row(align=True) + row.prop(props, "ifc_file") + row.operator("bim.select_bimtester_ifc_file", icon="FILE_FOLDER", text="") - if bimtester_properties.features_dir: - row = layout.row(align=True) - row.prop(bimtester_properties, "features_file") + row = self.layout.row(align=True) + row.prop(props, "feature") + row.operator("bim.select_feature", icon="FILE_FOLDER", text="") - row = layout.row(align=True) - row.prop(bimtester_properties, "scenario") - else: - return + has_ifc_file = (IfcStore.get_file() and props.should_load_from_memory) or ( + props.ifc_file and not props.should_load_from_memory + ) - if str(context.scene.BimTesterProperties.features_file) != '': # To handle the error when no .feature file exists in the folder - row = layout.row() - row.operator("bim.execute_bim_tester") + row = self.layout.row(align=True) + row.operator("bim.execute_bim_tester") + row.operator("bim.bim_tester_purge") - row = layout.row() - row.operator("bim.bim_tester_purge") + if props.feature and has_ifc_file: + self.layout.label(text="Scenario Authoring:") - layout.label(text="Quality Auditing:") + row = self.layout.row(align=True) + row.prop(props, "scenario") - row = layout.row() - row.prop(bimtester_properties, "qa_reject_element_reason") - row = layout.row() + row = self.layout.row() + row.prop(props, "qa_reject_element_reason") + row = self.layout.row() row.operator("bim.reject_element") - row = layout.row() - row.prop(bimtester_properties, "audit_ifc_class") + row = self.layout.row() + row.prop(props, "audit_ifc_class") - row = layout.row(align=True) + row = self.layout.row(align=True) row.operator("bim.approve_class") row.operator("bim.reject_class") - row = layout.row() + row = self.layout.row() row.operator("bim.select_audited")