mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-28 11:18:18 +00:00
New type of IfcTester report which doesn't require audit to be run to see a spreadsheet view of requirements
This commit is contained in:
@@ -26,7 +26,7 @@ from . import reporter
|
|||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Uses an IDS to audit an IFC")
|
parser = argparse.ArgumentParser(description="Uses an IDS to audit an IFC")
|
||||||
parser.add_argument("ids", type=str, help="Path to an IDS")
|
parser.add_argument("ids", type=str, help="Path to an IDS")
|
||||||
parser.add_argument("ifc", type=str, help="Path to an IFC")
|
parser.add_argument("ifc", type=str, help="Path to an IFC", nargs="?")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-r", "--reporter", type=str, help="The reporting method to view audit results", default="Console"
|
"-r", "--reporter", type=str, help="The reporting method to view audit results", default="Console"
|
||||||
)
|
)
|
||||||
@@ -41,14 +41,14 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
start = time.time()
|
|
||||||
specs = ids.open(args.ids)
|
specs = ids.open(args.ids)
|
||||||
ifc = ifcopenshell.open(args.ifc)
|
if args.ifc:
|
||||||
print("Finished loading:", time.time() - start)
|
start = time.time()
|
||||||
start = time.time()
|
ifc = ifcopenshell.open(args.ifc)
|
||||||
specs.validate(ifc)
|
print("Finished loading:", time.time() - start)
|
||||||
print("Finished validating:", time.time() - start)
|
start = time.time()
|
||||||
start = time.time()
|
specs.validate(ifc)
|
||||||
|
print("Finished validating:", time.time() - start)
|
||||||
|
|
||||||
if args.reporter == "Console":
|
if args.reporter == "Console":
|
||||||
engine = reporter.Console(specs, use_colour=not args.no_color)
|
engine = reporter.Console(specs, use_colour=not args.no_color)
|
||||||
@@ -60,6 +60,8 @@ elif args.reporter == "Html":
|
|||||||
engine = reporter.Html(specs)
|
engine = reporter.Html(specs)
|
||||||
elif args.reporter == "Ods":
|
elif args.reporter == "Ods":
|
||||||
engine = reporter.Ods(specs, excel_safe=args.excel_safe)
|
engine = reporter.Ods(specs, excel_safe=args.excel_safe)
|
||||||
|
elif args.reporter == "OdsSummary":
|
||||||
|
engine = reporter.OdsSummary(specs, excel_safe=args.excel_safe)
|
||||||
elif args.reporter == "Bcf":
|
elif args.reporter == "Bcf":
|
||||||
engine = reporter.Bcf(specs)
|
engine = reporter.Bcf(specs)
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ class ResultsSpecification(TypedDict):
|
|||||||
|
|
||||||
|
|
||||||
class ResultsRequirement(TypedDict):
|
class ResultsRequirement(TypedDict):
|
||||||
|
facet_type: str
|
||||||
|
label: str
|
||||||
|
value: str
|
||||||
description: str
|
description: str
|
||||||
status: bool
|
status: bool
|
||||||
failed_entities: list[ResultsFailedEntity]
|
failed_entities: list[ResultsFailedEntity]
|
||||||
@@ -291,8 +294,48 @@ class Json(Reporter):
|
|||||||
percent_pass = math.floor((total_pass / total_applicable) * 100) if total_applicable else "N/A"
|
percent_pass = math.floor((total_pass / total_applicable) * 100) if total_applicable else "N/A"
|
||||||
total_checks += total_applicable
|
total_checks += total_applicable
|
||||||
total_checks_pass += total_pass
|
total_checks_pass += total_pass
|
||||||
|
facet_type = type(requirement).__name__
|
||||||
|
value = ""
|
||||||
|
if facet_type == "Entity":
|
||||||
|
if requirement.predefinedType:
|
||||||
|
label = "IFC Class / Predefined Type"
|
||||||
|
value = f"{requirement.name}.{requirement.predefinedType}"
|
||||||
|
else:
|
||||||
|
label = "IFC Class"
|
||||||
|
value = requirement.name
|
||||||
|
elif facet_type == "Attribute":
|
||||||
|
label = requirement.name
|
||||||
|
if requirement.value:
|
||||||
|
value = requirement.value
|
||||||
|
elif facet_type == "Classification":
|
||||||
|
if requirement.system and requirement.value:
|
||||||
|
label = "System / Reference"
|
||||||
|
value = f"{requirement.system} / {requirement.value}"
|
||||||
|
elif requirement.system:
|
||||||
|
label = "System"
|
||||||
|
value = requirement.system
|
||||||
|
elif requirement.value:
|
||||||
|
label = "Reference"
|
||||||
|
value = requirement.value
|
||||||
|
elif facet_type == "PartOf":
|
||||||
|
label = requirement.relation
|
||||||
|
if requirement.predefinedType:
|
||||||
|
value = f"{requirement.name}.{requirement.predefinedType}"
|
||||||
|
else:
|
||||||
|
value = requirement.name
|
||||||
|
elif facet_type == "Property":
|
||||||
|
label = f"{requirement.propertySet}.{requirement.baseName}"
|
||||||
|
if requirement.value:
|
||||||
|
value = requirement.value
|
||||||
|
elif facet_type == "Material":
|
||||||
|
label = "Name / Category"
|
||||||
|
if requirement.value:
|
||||||
|
value = requirement.value
|
||||||
requirements.append(
|
requirements.append(
|
||||||
ResultsRequirement(
|
ResultsRequirement(
|
||||||
|
facet_type=facet_type,
|
||||||
|
label=label,
|
||||||
|
value=value,
|
||||||
description=requirement.to_string("requirement", specification, requirement),
|
description=requirement.to_string("requirement", specification, requirement),
|
||||||
status=requirement.status,
|
status=requirement.status,
|
||||||
failed_entities=self.report_failed_entities(requirement),
|
failed_entities=self.report_failed_entities(requirement),
|
||||||
@@ -527,6 +570,128 @@ class Ods(Json):
|
|||||||
self.doc.save(filepath, addsuffix=not filepath.lower().endswith(".ods"))
|
self.doc.save(filepath, addsuffix=not filepath.lower().endswith(".ods"))
|
||||||
|
|
||||||
|
|
||||||
|
class OdsSummary(Json):
|
||||||
|
def __init__(self, ids: Ids, excel_safe=False):
|
||||||
|
super().__init__(ids)
|
||||||
|
self.excel_safe = excel_safe
|
||||||
|
self.colours = {
|
||||||
|
"h": "cccccc", # Header
|
||||||
|
"p": "97cc64", # Pass
|
||||||
|
"f": "fb5a3e", # Fail
|
||||||
|
"t": "ffffff", # Regular text
|
||||||
|
}
|
||||||
|
|
||||||
|
def excel_safe_spreadsheet_name(self, name: str) -> str:
|
||||||
|
if not self.excel_safe:
|
||||||
|
return name
|
||||||
|
|
||||||
|
warning = (
|
||||||
|
f'WARNING. Sheet name "{name}" is not valid for Excel and will be changed. '
|
||||||
|
"See: https://support.microsoft.com/en-us/office/rename-a-worksheet-3f1f7148-ee83-404d-8ef0-9ff99fbad1f9"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not name or name == "History":
|
||||||
|
print(warning)
|
||||||
|
return "placeholder spreadsheet name"
|
||||||
|
|
||||||
|
if name.startswith("'") or name.endswith("'"):
|
||||||
|
print(warning)
|
||||||
|
name = name.strip("'")
|
||||||
|
|
||||||
|
pattern = r"[\\\/\?\*\:\[\]]"
|
||||||
|
if re.search(pattern, name):
|
||||||
|
name = re.sub(pattern, "", name)
|
||||||
|
print(warning)
|
||||||
|
|
||||||
|
if len(name) > 31:
|
||||||
|
name = name[:31]
|
||||||
|
print(warning)
|
||||||
|
return name
|
||||||
|
|
||||||
|
def to_file(self, filepath: str) -> None:
|
||||||
|
from odf.opendocument import OpenDocumentSpreadsheet
|
||||||
|
from odf.style import Style, TableCellProperties
|
||||||
|
from odf.table import Table, TableRow, TableCell
|
||||||
|
from odf.text import P
|
||||||
|
|
||||||
|
self.doc = OpenDocumentSpreadsheet()
|
||||||
|
|
||||||
|
self.cell_formats = {}
|
||||||
|
for key, value in self.colours.items():
|
||||||
|
style = Style(name=key, family="table-cell")
|
||||||
|
style.addElement(TableCellProperties(backgroundcolor="#" + value))
|
||||||
|
self.doc.automaticstyles.addElement(style)
|
||||||
|
self.cell_formats[key] = style
|
||||||
|
|
||||||
|
table = Table(name=self.excel_safe_spreadsheet_name(self.results["title"]))
|
||||||
|
tr = TableRow()
|
||||||
|
for header in ["Specification", "Applicability", "Facet Type", "Data Name", "Value Requirements"]:
|
||||||
|
tc = TableCell(valuetype="string", stylename="h")
|
||||||
|
tc.addElement(P(text=header))
|
||||||
|
tr.addElement(tc)
|
||||||
|
table.addElement(tr)
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for specification in self.results["specifications"]:
|
||||||
|
applicability = ", ".join(specification["applicability"])
|
||||||
|
for requirement in specification["requirements"]:
|
||||||
|
rows.append(
|
||||||
|
[
|
||||||
|
specification["name"],
|
||||||
|
applicability,
|
||||||
|
requirement["facet_type"],
|
||||||
|
requirement["label"],
|
||||||
|
requirement["value"],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
tr = TableRow()
|
||||||
|
c = 0
|
||||||
|
for col in row:
|
||||||
|
tc = TableCell(valuetype="string")
|
||||||
|
if col is None:
|
||||||
|
col = "NULL"
|
||||||
|
tc.addElement(P(text=col))
|
||||||
|
tr.addElement(tc)
|
||||||
|
c += 1
|
||||||
|
table.addElement(tr)
|
||||||
|
self.doc.spreadsheet.addElement(table)
|
||||||
|
|
||||||
|
while False:
|
||||||
|
for requirement in specification["requirements"]:
|
||||||
|
if requirement["status"]:
|
||||||
|
continue
|
||||||
|
for failure in requirement["failed_entities"]:
|
||||||
|
element = failure.get("element", None)
|
||||||
|
element_type = failure.get("element_type", None)
|
||||||
|
row = [
|
||||||
|
requirement["description"],
|
||||||
|
failure.get("reason", "No reason provided"),
|
||||||
|
failure["class"],
|
||||||
|
failure["predefined_type"],
|
||||||
|
failure["name"],
|
||||||
|
failure["description"],
|
||||||
|
failure["global_id"],
|
||||||
|
failure["tag"],
|
||||||
|
str(element) if element else "N/A",
|
||||||
|
str(element_type) if element_type else "N/A",
|
||||||
|
]
|
||||||
|
tr = TableRow()
|
||||||
|
c = 0
|
||||||
|
for col in row:
|
||||||
|
tc = TableCell(valuetype="string", stylename="t")
|
||||||
|
if col is None:
|
||||||
|
col = "NULL"
|
||||||
|
tc.addElement(P(text=col))
|
||||||
|
tr.addElement(tc)
|
||||||
|
c += 1
|
||||||
|
table.addElement(tr)
|
||||||
|
self.doc.spreadsheet.addElement(table)
|
||||||
|
|
||||||
|
self.doc.save(filepath, addsuffix=not filepath.lower().endswith(".ods"))
|
||||||
|
|
||||||
|
|
||||||
class Bcf(Json):
|
class Bcf(Json):
|
||||||
def report_failed_entities(self, requirement: Facet) -> list[FacetFailure]:
|
def report_failed_entities(self, requirement: Facet) -> list[FacetFailure]:
|
||||||
return [FacetFailure(f) for f in requirement.failures]
|
return [FacetFailure(f) for f in requirement.failures]
|
||||||
|
|||||||
Reference in New Issue
Block a user