diff --git a/src/ifctester/ifctester/facet.py b/src/ifctester/ifctester/facet.py index bec92041f9..9b3187ab39 100644 --- a/src/ifctester/ifctester/facet.py +++ b/src/ifctester/ifctester/facet.py @@ -42,6 +42,7 @@ def cast_to_value(from_value, to_value): class Facet: def __init__(self, *parameters): + self.status = None self.failed_entities = [] self.failed_reasons = [] for i, name in enumerate(self.parameters): diff --git a/src/ifctester/ifctester/ids.py b/src/ifctester/ifctester/ids.py index 51fb043a8d..4b8afaf3c5 100644 --- a/src/ifctester/ifctester/ids.py +++ b/src/ifctester/ifctester/ids.py @@ -217,7 +217,8 @@ class Specification: self.applicable_entities.append(element) for facet in self.requirements: result = facet(element) - if not bool(result): + facet.status = bool(result) + if not facet.status: self.failed_entities.add(element) facet.failed_entities.append(element) facet.failed_reasons.append(str(result)) @@ -227,5 +228,7 @@ class Specification: self.status = False elif self.minOccurs != 0 and not self.applicable_entities: self.status = False + for facet in self.requirements: + facet.status = False elif len(self.applicable_entities) > (self.maxOccurs or 1): self.status = False diff --git a/src/ifctester/ifctester/reporter.py b/src/ifctester/ifctester/reporter.py index 6148526033..6a98ecc0c0 100644 --- a/src/ifctester/ifctester/reporter.py +++ b/src/ifctester/ifctester/reporter.py @@ -28,10 +28,10 @@ class Reporter: def report(self, ids): pass - def to_string(): + def to_string(self): return "" - def write(filepath): + def write(self, filepath): pass @@ -142,7 +142,7 @@ class Json(Reporter): requirements.append( { "description": requirement.to_string("requirement"), - "success": not requirement.failed_entities, + "status": requirement.status, "failed_entities": [ {"reason": requirement.failed_reasons[i], "element": str(e)} for i, e in enumerate(requirement.failed_entities[0:10]) diff --git a/src/ifctester/webapp/app.py b/src/ifctester/webapp/app.py new file mode 100644 index 0000000000..ca79f7848d --- /dev/null +++ b/src/ifctester/webapp/app.py @@ -0,0 +1,77 @@ +# IfcTester - IDS based model auditing +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcTester. +# +# IfcTester is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcTester 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcTester. If not, see . + + +import os +import time +import ifctester +import ifctester.reporter +import ifcopenshell +from flask import Flask, request, send_from_directory + +app = Flask(__name__) + + +class Ifc: + ifc = None + filepath = None + + @classmethod + def get(cls, filepath=None): + if filepath is None or filepath == cls.filepath: + return cls.ifc + cls.filepath = filepath + cls.ifc = ifcopenshell.open(filepath) + return cls.ifc + + +@app.route("/") +def index(): + with open("www/index.html") as template: + return template.read() + + +@app.route("/.") +def get_asset(asset, ext): + if ext in ("js", "css"): + return send_from_directory("www", asset + "." + ext) + + +@app.route("/audit", methods=["POST"]) +def audit(): + filename = ifcopenshell.guid.new() + ids_filepath = os.path.join("uploads", filename + ".ids") + ifc_filepath = os.path.join("uploads", filename + ".ifc") + request.files.get("ids").save(ids_filepath) + request.files.get("ifc").save(ifc_filepath) + + start = time.time() + specs = ifctester.open(ids_filepath) + ifc = Ifc.get(ifc_filepath) + print("Finished loading:", time.time() - start) + start = time.time() + specs.validate(ifc) + print("Finished validating:", time.time() - start) + start = time.time() + + os.remove(ids_filepath) + os.remove(ifc_filepath) + + engine = ifctester.reporter.Json(specs) + engine.report() + return engine.to_string() diff --git a/src/ifctester/webapp/www/app.js b/src/ifctester/webapp/www/app.js index fe56432c2a..9514820818 100644 --- a/src/ifctester/webapp/www/app.js +++ b/src/ifctester/webapp/www/app.js @@ -10,6 +10,7 @@ class IDSContainer extends HTMLElement { this.filename = 'specifications.ids'; this.ids = null; this.containerId = crypto.randomUUID(); + this.isEditing = true; } } @@ -315,6 +316,13 @@ class IDSFacets extends HTMLElement { } feather.replace(); } + + showResults(requirements) { + var facetElements = this.getElementsByTagName('ids-facet'); + for (var i=0; i SAVE - - AUDIT MODEL - + + + AUDIT MODEL + + CLOSE @@ -139,6 +141,13 @@ + + Optionally write instructions about how to achieve this requirement. @@ -163,7 +172,7 @@ - + diff --git a/src/ifctester/webapp/www/style.css b/src/ifctester/webapp/www/style.css index 382aa1f57b..fd86d02296 100644 --- a/src/ifctester/webapp/www/style.css +++ b/src/ifctester/webapp/www/style.css @@ -270,20 +270,30 @@ ids-spec ids-spec-handle .snippet { bottom: -5px; position: relative; } -.result-icon { +ids-result { display: inline-block; position: absolute; margin-left: -30px; - margin-top: -2px; + margin-top: -3px; cursor: help; } -.result-icon.pass { +ids-result span { + position: absolute; + float: right; + right: 30px; + background-color: #333; + color: white; + font-family: 'Nova Mono'; + padding: 3px; + border-radius: 5px; +} +ids-result[name="pass"] { color: var(--green); } -.result-icon.fail { +ids-result[name="fail"] { color: var(--red); } -.result-icon:hover { +ids-result:hover { color: var(--blue); } .hidden {