ifcopenshell.validate to validate guids #5808

This commit is contained in:
Andrej730
2024-11-27 11:36:48 +05:00
parent cc014bb7ec
commit 25eecded4e
@@ -36,6 +36,7 @@ import os
import sys import sys
import json import json
import functools import functools
import types
from collections import namedtuple from collections import namedtuple
from typing import Union, Iterator, Any, Optional from typing import Union, Iterator, Any, Optional
@@ -403,8 +404,8 @@ def validate(f: Union[ifcopenshell.file, str], logger: Logger, express_rules=Fal
if hasattr(logger, "set_state"): if hasattr(logger, "set_state"):
logger.set_state("instance", inst) logger.set_state("instance", inst)
if hasattr(inst, "GlobalId"): guid: Union[str, None, types.EllipsisType]
guid = inst.GlobalId if (guid := getattr(inst, "GlobalId", ...)) is not ...:
if guid is not None and guid in used_guids: if guid is not None and guid in used_guids:
rule = "Rule IfcRoot.UR1:\n The attribute GlobalId should be unique" rule = "Rule IfcRoot.UR1:\n The attribute GlobalId should be unique"
previous_element = used_guids[guid] previous_element = used_guids[guid]
@@ -417,7 +418,19 @@ def validate(f: Union[ifcopenshell.file, str], logger: Logger, express_rules=Fal
annotate_inst_attr_pos(previous_element, 0), annotate_inst_attr_pos(previous_element, 0),
) )
else: else:
used_guids[guid] = inst if guid is not None:
if (validation_error := validate_guid(guid)) is None:
used_guids[guid] = inst
else:
rule = "IfcGloballyUniqueId base64 validation:\n The attribute GlobalId should be valid base64 encoded 128-bit number."
previous_element = None
logger.error(
"On instance:\n %s\n %s\n%s\nViolated by:\n %s\n",
inst,
annotate_inst_attr_pos(inst, 0),
rule,
validation_error,
)
entity, attrs = get_entity_attributes(schema, inst.is_a()) entity, attrs = get_entity_attributes(schema, inst.is_a())
@@ -528,6 +541,28 @@ def validate(f: Union[ifcopenshell.file, str], logger: Logger, express_rules=Fal
ifcopenshell.express.rule_executor.run(f, logger) ifcopenshell.express.rule_executor.run(f, logger)
def validate_guid(guid: str) -> Union[str, None]:
"""Check if a given guid is valid.
Don't check for `None` as `None` guid will trigger "non-optional" validation error either way.
:return: `None` if guid is valid, otherwise a string with an error message.
"""
if len(guid) != 22:
return "Guid length should be 22 characters."
if guid[0] not in "0123":
return "Guid first character must be either a 0, 1, 2, or 3."
try:
ifcopenshell.guid.expand(guid)
except:
allowed_characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$"
if any(c for c in guid if c not in allowed_characters):
return "Guid contains invalid characters, allowed characters: '%s'." % allowed_characters
# NOTE: are there actually cases where guid won't expand, besides invalid characters?
return "Couldn't decompress guid, it's not base64 encoded."
return None
def validate_ifc_header(f: ifcopenshell.file, logger: Logger) -> None: def validate_ifc_header(f: ifcopenshell.file, logger: Logger) -> None:
header: W.IfcSpfHeader = f.wrapped_data.header header: W.IfcSpfHeader = f.wrapped_data.header
AGGREGATE_TYPE = "LIST [ 1 : ? ] OF STRING (256)" AGGREGATE_TYPE = "LIST [ 1 : ? ] OF STRING (256)"