From dde847cb5a4fc2de2769f28877bb93d0aeba6a64 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 20 Feb 2023 17:50:14 +1100 Subject: [PATCH] IfcTester can now output reports in BCF --- src/ifctester/ifctester/__main__.py | 2 + src/ifctester/ifctester/reporter.py | 125 +++++++--------------------- 2 files changed, 32 insertions(+), 95 deletions(-) diff --git a/src/ifctester/ifctester/__main__.py b/src/ifctester/ifctester/__main__.py index 591b053421..c5c70cd9c2 100644 --- a/src/ifctester/ifctester/__main__.py +++ b/src/ifctester/ifctester/__main__.py @@ -57,6 +57,8 @@ elif args.reporter == "Html": engine = reporter.Html(specs) elif args.reporter == "Ods": engine = reporter.Ods(specs) +elif args.reporter == "Bcf": + engine = reporter.Bcf(specs) engine.report() diff --git a/src/ifctester/ifctester/reporter.py b/src/ifctester/ifctester/reporter.py index 0c8613ac53..ae9e0cd479 100644 --- a/src/ifctester/ifctester/reporter.py +++ b/src/ifctester/ifctester/reporter.py @@ -169,10 +169,7 @@ class Json(Reporter): { "description": requirement.to_string("requirement"), "status": requirement.status, - "failed_entities": [ - {"reason": requirement.failed_reasons[i], "element": str(e)} - for i, e in enumerate(requirement.failed_entities[0:10]) - ], + "failed_entities": self.report_failed_entities(requirement), } ) total = len(specification.applicable_entities) @@ -188,6 +185,12 @@ class Json(Reporter): "requirements": requirements, } + def report_failed_entities(self, requirement): + return [ + {"reason": requirement.failed_reasons[i], "element": str(e)} + for i, e in enumerate(requirement.failed_entities) + ] + def to_string(self): import json @@ -321,97 +324,29 @@ class Ods(Json): self.doc.save(filepath, True) -class Bcf(Reporter): - def __init__(self, ids, with_viewpoint=True, filepath=None, group=False): - super().__init__(ids) +class Bcf(Json): + def report_failed_entities(self, requirement): + return [ + {"reason": requirement.failed_reasons[i], "element": e} for i, e in enumerate(requirement.failed_entities) + ] + def to_file(self, filepath): from bcf.v2.bcfxml import BcfXml - self.with_viewpoint = with_viewpoint - self.group = group - self.bcf = BcfXml() - self.bcf.new_project() - if not filepath: - self.filepath=os.path.join(sys.path[0], self.ids.info['title'] + '.bcf') - # self.bcf.filepath = sys.path[0] - self.bcf.project.name = self.ids.info.get("title", "Untitled IDS") - self.bcf.author = self.ids.info.get("author", "your@email.com") - self.bcf.creation_date = datetime.datetime.today().replace(microsecond=0).isoformat() - self.bcf.modified_date = datetime.datetime.today().replace(microsecond=0).isoformat() - - def report(self): - for specification in self.ids.specifications: - self.report_specification(specification, save_project=False) - self.bcf.save_project(self.filepath) - - def report_specification(self, specification, save_project=True): - from bcf.v2 import data as bcf - for requirement in specification.requirements: - if self.group: - topic = bcf.Topic() - topic.title = requirement.to_string("requirement") - topic.description = ";\n".join([requirement.failed_reasons[i] + " for " + str(e.get_info()['type']) + ": " + str(e.GlobalId) for i, e in enumerate(requirement.failed_entities)]) - self.bcf.add_topic(topic) - if self.with_viewpoint: - self.add_viewpoint(topic, requirement) - else: - for i, e in enumerate(requirement.failed_entities): - topic = bcf.Topic() - topic.title = requirement.to_string("requirement") - topic.description = requirement.failed_reasons[i] + " for " + str(e.get_info()['type']) + ": " + str(e.GlobalId) - self.bcf.add_topic(topic) - if self.with_viewpoint: - self.add_viewpoint(topic, requirement) - if save_project: - self.bcf.save_project(self.filepath) - - def add_viewpoint(self, topic, requirement): - import numpy as np - import ifcopenshell.util.placement - from bcf.v2 import data as bcf - - viewpoint = bcf.Viewpoint() - viewpoint.perspective_camera = bcf.PerspectiveCamera() - ifc_elem = requirement.failed_entities[0] - target_position = np.array(ifcopenshell.util.placement.get_local_placement(ifc_elem.ObjectPlacement)) - target_position = target_position[:, 3][0:3] - camera_position = target_position + np.array((5, 5, 5)) - viewpoint.perspective_camera.camera_view_point.x = camera_position[0] - viewpoint.perspective_camera.camera_view_point.y = camera_position[1] - viewpoint.perspective_camera.camera_view_point.z = camera_position[2] - camera_direction = camera_position - target_position - camera_direction = camera_direction / np.linalg.norm(camera_direction) - camera_right = np.cross(np.array([0.0, 0.0, 1.0]), camera_direction) - camera_right = camera_right / np.linalg.norm(camera_right) - camera_up = np.cross(camera_direction, camera_right) - camera_up = camera_up / np.linalg.norm(camera_up) - rotation_transform = np.zeros((4, 4)) - rotation_transform[0, :3] = camera_right - rotation_transform[1, :3] = camera_up - rotation_transform[2, :3] = camera_direction - rotation_transform[-1, -1] = 1 - translation_transform = np.eye(4) - translation_transform[:3, -1] = -camera_position - look_at_transform = np.matmul(rotation_transform, translation_transform) - mat = np.linalg.inv(look_at_transform) - viewpoint.perspective_camera.camera_direction.x = mat[0][2] * -1 - viewpoint.perspective_camera.camera_direction.y = mat[1][2] * -1 - viewpoint.perspective_camera.camera_direction.z = mat[2][2] * -1 - viewpoint.perspective_camera.camera_up_vector.x = mat[0][1] - viewpoint.perspective_camera.camera_up_vector.y = mat[1][1] - viewpoint.perspective_camera.camera_up_vector.z = mat[2][1] - viewpoint.components = bcf.Components() - for e in requirement.failed_entities: - c = bcf.Component() - c.ifc_guid = e.GlobalId - viewpoint.components.selection.append(c) - viewpoint.components.visibility = bcf.ComponentVisibility() - viewpoint.components.visibility.default_visibility = True - # add single pixel png file as a snapshot, as viewpoints without snapshots won't open in viewers - png = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x01\x03\x00\x00\x00%\xdbV\xca\x00\x00\x00\x03PLTE\x00\x00\x00\xa7z=\xda\x00\x00\x00\x01tRNS\x00@\xe6\xd8f\x00\x00\x00\nIDAT\x08\xd7c`\x00\x00\x00\x02\x00\x01\xe2!\xbc3\x00\x00\x00\x00IEND\xaeB`\x82" - snapshot_path = os.path.join(self.bcf.filepath, topic.guid, topic.guid+".png") - snapshot = open(snapshot_path, "wb") - snapshot.write(png) - snapshot.close() - viewpoint.snapshot = topic.guid+".png" - self.bcf.add_viewpoint(topic, viewpoint) + bcfxml = BcfXml.create_new(self.results["title"]) + for specification in self.results["specifications"]: + if specification["status"]: + continue + for requirement in specification["requirements"]: + if requirement["status"]: + continue + for failure in requirement["failed_entities"]: + element = failure["element"] + title = f"{element.id()}/{element.is_a()}/" + title += getattr(element, "Name", None) or "Unnamed" + title += " - " + failure.get("reason", "No reason") + description = f'{specification["name"]} - {requirement["description"]}' + topic = bcfxml.add_topic(title, description, "IfcTester") + if element.is_a("IfcElement"): + topic.add_viewpoint(element) + bcfxml.save_project(filepath)