mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
mvd info to module, add testing, setattr & getattr
This commit is contained in:
committed by
Thomas Krijnen
parent
9f11568aff
commit
cbe8fc1ebd
@@ -37,9 +37,7 @@ from typing import Any
|
|||||||
from . import ifcopenshell_wrapper
|
from . import ifcopenshell_wrapper
|
||||||
from .entity_instance import entity_instance
|
from .entity_instance import entity_instance
|
||||||
|
|
||||||
from lark import Lark, Transformer
|
from ifcopenshell.util.mvd_info import MvdInfo, LARK_AVAILABLE
|
||||||
from lark.exceptions import UnexpectedCharacters, UnexpectedEOF, UnexpectedToken
|
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
@@ -211,106 +209,6 @@ NO_HEADER = ifcopenshell_wrapper.file_open_status.NO_HEADER
|
|||||||
UNSUPPORTED_SCHEMA = ifcopenshell_wrapper.file_open_status.UNSUPPORTED_SCHEMA
|
UNSUPPORTED_SCHEMA = ifcopenshell_wrapper.file_open_status.UNSUPPORTED_SCHEMA
|
||||||
INVALID_SYNTAX = ifcopenshell_wrapper.file_open_status.INVALID_SYNTAX
|
INVALID_SYNTAX = ifcopenshell_wrapper.file_open_status.INVALID_SYNTAX
|
||||||
|
|
||||||
mvd_grammar = r'''
|
|
||||||
start: entry+
|
|
||||||
|
|
||||||
entry: "ViewDefinition" "[" simple_value_list "]" -> view_definition
|
|
||||||
| "Comment" "[" comment_text "]" -> comment
|
|
||||||
| GENERIC_KEYWORD "[" value_list_set "]" -> dynamic_option
|
|
||||||
|
|
||||||
%declare COMMENT_TEXT // Ensure Lark treats it with higher priority
|
|
||||||
|
|
||||||
GENERIC_KEYWORD: /[A-Za-z0-9_]+/
|
|
||||||
|
|
||||||
simple_value_list: value ("," value)*
|
|
||||||
|
|
||||||
value_list_set: value_set (";" value_set)*
|
|
||||||
|
|
||||||
value_set: set_name ":" simple_value_list
|
|
||||||
|
|
||||||
set_name: /[A-Za-z0-9_]+/
|
|
||||||
|
|
||||||
value: /[A-Za-z0-9 _\.-]+/
|
|
||||||
|
|
||||||
comment_text: /[^\[\]]+/ // Free text inside COMMENT brackets
|
|
||||||
|
|
||||||
%import common.WS
|
|
||||||
%ignore WS
|
|
||||||
'''
|
|
||||||
|
|
||||||
parser = Lark(mvd_grammar, parser='lalr')
|
|
||||||
class DescriptionTransform(Transformer):
|
|
||||||
def __init__(self):
|
|
||||||
self.mvd = []
|
|
||||||
self.keywords = set()
|
|
||||||
self.comments = ""
|
|
||||||
self.exchangerequirement = ""
|
|
||||||
self.option = ''
|
|
||||||
|
|
||||||
|
|
||||||
def view_definition(self, args):
|
|
||||||
self.keywords.add('mvd')
|
|
||||||
self.mvd.extend(args[0])
|
|
||||||
|
|
||||||
|
|
||||||
def dynamic_option(self, args):
|
|
||||||
"""
|
|
||||||
e.g. in case of 'Remark' as optional keyword in the description
|
|
||||||
The value can be retrieved through DescriptionTransform.remark
|
|
||||||
"""
|
|
||||||
key = str(args[0]).lower()
|
|
||||||
attr_name = f"{key}"
|
|
||||||
if attr_name not in self.keywords:
|
|
||||||
setattr(self, attr_name, {})
|
|
||||||
self.keywords.add(attr_name)
|
|
||||||
dynamic_dict = getattr(self, attr_name)
|
|
||||||
for value_set in args[1]:
|
|
||||||
set_name, *values = value_set
|
|
||||||
dynamic_dict[set_name] = values if len(values) > 1 else values[0]
|
|
||||||
|
|
||||||
|
|
||||||
def comment(self, args):
|
|
||||||
self.keywords.add('comment')
|
|
||||||
self.comments = " ".join(str(child) for child in args[0].children).strip()
|
|
||||||
|
|
||||||
def simple_value_list(self, args):
|
|
||||||
return [str(arg) for arg in args]
|
|
||||||
|
|
||||||
def value_list_set(self, args):
|
|
||||||
return args
|
|
||||||
|
|
||||||
def value_set(self, args):
|
|
||||||
return [str(args[0])] + args[1]
|
|
||||||
|
|
||||||
def value(self, args):
|
|
||||||
return str(args[0])
|
|
||||||
|
|
||||||
def set_name(self, args):
|
|
||||||
return str(args[0])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def other_keywords(self):
|
|
||||||
""""
|
|
||||||
The predefined keywords are 'ViewDefinition', 'Option', 'Comment', 'ExchangeRequirement' and 'Option'
|
|
||||||
Keywords in the description not from this lists are returned
|
|
||||||
"""
|
|
||||||
return {k for k in self.keywords if k not in {'mvd', 'comment', 'exchangerequirement', 'option'}}
|
|
||||||
|
|
||||||
|
|
||||||
def parse_mvd(description):
|
|
||||||
text = ' '.join(description)
|
|
||||||
parser = Lark(mvd_grammar, parser='lalr')
|
|
||||||
parsed_description = DescriptionTransform()
|
|
||||||
try:
|
|
||||||
if not text:
|
|
||||||
parsed_description.mvd = None
|
|
||||||
return parsed_description
|
|
||||||
parse_tree = parser.parse(text)
|
|
||||||
parsed_description.transform(parse_tree)
|
|
||||||
except (UnexpectedCharacters, UnexpectedEOF, UnexpectedToken) as e:
|
|
||||||
parsed_description.mvd = None
|
|
||||||
return parsed_description
|
|
||||||
|
|
||||||
class file:
|
class file:
|
||||||
"""Base class for containing IFC files.
|
"""Base class for containing IFC files.
|
||||||
|
|
||||||
@@ -409,7 +307,6 @@ class file:
|
|||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
file_dict[self.file_pointer()] = weakref.ref(self)
|
file_dict[self.file_pointer()] = weakref.ref(self)
|
||||||
self.parsed_description = parse_mvd(self.wrapped_data.header.file_description.description)
|
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def __del__(self) -> None:
|
||||||
# Avoid infinite recursion if file is failed to initialize
|
# Avoid infinite recursion if file is failed to initialize
|
||||||
@@ -569,68 +466,16 @@ class file:
|
|||||||
number = re.search(prefix + r"(\d)", schema)
|
number = re.search(prefix + r"(\d)", schema)
|
||||||
version.append(int(number.group(1)) if number else 0)
|
version.append(int(number.group(1)) if number else 0)
|
||||||
return tuple(version)
|
return tuple(version)
|
||||||
|
|
||||||
@property
|
|
||||||
def mvd(self) -> str:
|
|
||||||
"""
|
|
||||||
View Definition supported by the exporting application that is reflected in the IFC file
|
|
||||||
For example:
|
|
||||||
“CoordinationView”
|
|
||||||
| “PresentationView”
|
|
||||||
| “StructuralAnalysisView”
|
|
||||||
| “FMHandOverView”
|
|
||||||
| “QuantityTakeOffAddOnView”
|
|
||||||
| “SpaceBoundary1stLevelAddOnView”
|
|
||||||
| “SpaceBoundary2ndLevelAddOnView”
|
|
||||||
"""
|
|
||||||
return ','.join(self.parsed_description.mvd)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mvd_comments(self) -> str:
|
|
||||||
"""
|
|
||||||
For example; 'Comment [This
|
|
||||||
export contains Boolean Operation geometry that may not be fully
|
|
||||||
supported by other application]'
|
|
||||||
|
|
||||||
f.mvd_comments
|
|
||||||
'This export contains Boolean Operation geometry that may not be fully supported by other application'
|
|
||||||
"""
|
|
||||||
return str(self.parsed_description.comments)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mvd_exchange_requirements(self) -> str:
|
|
||||||
return str(self.parsed_description.exchangerequirement)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mvd_options(self) -> str:
|
|
||||||
"""
|
|
||||||
Option being used by the exporting application for e.g. quality control and debugging
|
|
||||||
a. this is an optional field providing informal information, the values for the Option keyword
|
|
||||||
reflect the export settings of the exporting software application, those settings may be
|
|
||||||
specific to the exporting software
|
|
||||||
For example:
|
|
||||||
> "{'ExcludedObjects': [' Stair', ' Ramp', ' Space'], 'SplitLevel': ' On'}"
|
|
||||||
"""
|
|
||||||
return str(self.parsed_description.option)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mvd_keywords(self) -> str:
|
def mvd(self):
|
||||||
"""
|
if not LARK_AVAILABLE:
|
||||||
Returns all keywords from description, e.g.
|
return None
|
||||||
f.mvd_keywords
|
file_description = self.wrapped_data.header.file_description
|
||||||
"{'exchangerequirement', 'mvd', 'option', 'comment', 'remark'}"_
|
return MvdInfo(
|
||||||
"""
|
get_description=lambda: file_description.description,
|
||||||
return str(self.parsed_description.keywords)
|
set_description=lambda d: setattr(file_description, "description", tuple(d))
|
||||||
|
)
|
||||||
@property
|
|
||||||
def mvd_optional_keyword_fields(self) -> str:
|
|
||||||
""""
|
|
||||||
Returns fields from keywords other than 'ViewDefinition', 'Option', 'Comment' and 'ExchangeRequirement'.
|
|
||||||
For example, in case of
|
|
||||||
'REMARK [SomeKey: SomeValue; AnotherKey: AnotherValue]'
|
|
||||||
file.mvd_optional_keywords_fields == "{'remark': {'SomeKey': ' SomeValue', 'AnotherKey': ' AnotherValue'}}"
|
|
||||||
"""
|
|
||||||
return str({kw: getattr(self.parsed_description, kw) for kw in self.parsed_description.other_keywords})
|
|
||||||
|
|
||||||
def __getattr__(self, attr) -> Union[Any, Callable[..., ifcopenshell.entity_instance]]:
|
def __getattr__(self, attr) -> Union[Any, Callable[..., ifcopenshell.entity_instance]]:
|
||||||
if attr[0:6] == "create":
|
if attr[0:6] == "create":
|
||||||
|
|||||||
@@ -0,0 +1,310 @@
|
|||||||
|
try:
|
||||||
|
from lark import Lark, Transformer
|
||||||
|
from lark.exceptions import UnexpectedCharacters, UnexpectedEOF, UnexpectedToken
|
||||||
|
LARK_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
LARK_AVAILABLE = False
|
||||||
|
|
||||||
|
from typing import Callable
|
||||||
|
import re
|
||||||
|
|
||||||
|
if LARK_AVAILABLE:
|
||||||
|
mvd_grammar = r'''
|
||||||
|
start: entry+
|
||||||
|
|
||||||
|
entry: "ViewDefinition" "[" simple_value_list "]" -> view_definition
|
||||||
|
| "Comment" "[" simple_value_list "]" -> comment
|
||||||
|
| "ExchangeRequirement" "[" other_keyword "]" -> exchangerequirement
|
||||||
|
| "Option" "[" other_keyword "]" -> option
|
||||||
|
| GENERIC_KEYWORD "[" dynamic_option_word "]" -> dynamic_option
|
||||||
|
|
||||||
|
GENERIC_KEYWORD: /[A-Za-z0-9_]+/
|
||||||
|
|
||||||
|
simple_value_list: value ("," value)*
|
||||||
|
|
||||||
|
value_list_set: value_set (";" value_set)*
|
||||||
|
|
||||||
|
value_set: set_name ":" simple_value_list
|
||||||
|
|
||||||
|
set_name: /[A-Za-z0-9_]+/
|
||||||
|
|
||||||
|
value: /[A-Za-z0-9 _\.-]+/
|
||||||
|
|
||||||
|
other_keyword: /[^\[\]]+/
|
||||||
|
|
||||||
|
dynamic_option_word: /[^\[\]]+/
|
||||||
|
|
||||||
|
%import common.WS
|
||||||
|
%ignore WS
|
||||||
|
'''
|
||||||
|
|
||||||
|
parser = Lark(mvd_grammar, parser='lalr')
|
||||||
|
|
||||||
|
class DescriptionTransform(Transformer):
|
||||||
|
def __init__(self):
|
||||||
|
self.view_definitions = []
|
||||||
|
self.keywords = set()
|
||||||
|
self.comments = ""
|
||||||
|
self.exchange_requirements = ""
|
||||||
|
self.options = ""
|
||||||
|
self._dynamic = {}
|
||||||
|
|
||||||
|
def view_definition(self, args):
|
||||||
|
self.keywords.add('view_definitions')
|
||||||
|
self.view_definitions.extend(args[0])
|
||||||
|
|
||||||
|
def store_text_attribute(self, args, keyword):
|
||||||
|
self.keywords.add(keyword)
|
||||||
|
setattr(self, keyword, " ".join(" ".join(str(child) for child in args[0].children).split()))
|
||||||
|
|
||||||
|
def comment(self, args):
|
||||||
|
self.keywords.add("comments")
|
||||||
|
self.comments = args[0] if len(args[0]) > 1 else args[0][0]
|
||||||
|
|
||||||
|
def exchangerequirement(self, args):
|
||||||
|
self.store_text_attribute(args, "exchange_requirements")
|
||||||
|
|
||||||
|
def option(self, args):
|
||||||
|
if v := parse_semicolon_separated_kv(" ".join(" ".join(str(child) for child in args[0].children).split())):
|
||||||
|
setattr(self, 'options', v)
|
||||||
|
else:
|
||||||
|
self.store_text_attribute(args, "options")
|
||||||
|
|
||||||
|
def dynamic_option(self, args):
|
||||||
|
try:
|
||||||
|
original_keyword = str(args[0])
|
||||||
|
key = original_keyword.lower()
|
||||||
|
raw_text = args[1].children[0].value
|
||||||
|
parsed_value = parse_semicolon_separated_kv(raw_text)
|
||||||
|
self._dynamic[key] = (parsed_value, original_keyword)
|
||||||
|
self.keywords.add(key)
|
||||||
|
setattr(self, key, parsed_value)
|
||||||
|
except Exception:
|
||||||
|
setattr(self, key, None)
|
||||||
|
|
||||||
|
def simple_value_list(self, args):
|
||||||
|
return [str(arg) for arg in args]
|
||||||
|
|
||||||
|
def value_list_set(self, args):
|
||||||
|
return args
|
||||||
|
|
||||||
|
def value_set(self, args):
|
||||||
|
return [str(args[0])] + args[1]
|
||||||
|
|
||||||
|
def value(self, args):
|
||||||
|
return str(args[0])
|
||||||
|
|
||||||
|
def set_name(self, args):
|
||||||
|
return str(args[0])
|
||||||
|
|
||||||
|
def parse_mvd(description):
|
||||||
|
text = ' '.join(description)
|
||||||
|
parsed_description = DescriptionTransform()
|
||||||
|
try:
|
||||||
|
if not text:
|
||||||
|
parsed_description.view_definitions = None
|
||||||
|
return parsed_description
|
||||||
|
parse_tree = parser.parse(text)
|
||||||
|
parsed_description.transform(parse_tree)
|
||||||
|
except (UnexpectedCharacters, UnexpectedEOF, UnexpectedToken):
|
||||||
|
parsed_description.view_definitions = None
|
||||||
|
return parsed_description
|
||||||
|
|
||||||
|
def parse_semicolon_separated_kv(text: str) -> dict[str, str | list[str]] | None:
|
||||||
|
if not re.search(r'\w+\s*:\s*[^:]+', text):
|
||||||
|
return None
|
||||||
|
result = {}
|
||||||
|
try:
|
||||||
|
pairs = text.split(';')
|
||||||
|
for pair in pairs:
|
||||||
|
if ':' in pair:
|
||||||
|
key, value = pair.split(':', 1)
|
||||||
|
key = key.strip()
|
||||||
|
values = [v.strip() for v in value.split(',')]
|
||||||
|
result[key] = values[0] if len(values) == 1 else values
|
||||||
|
return result
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
def parse_mvd(description):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class MvdInfo:
|
||||||
|
def __init__(self, get_description: Callable[[], list[str]], set_description: Callable[[list[str]], None]):
|
||||||
|
self._get_description = get_description
|
||||||
|
self._set_description = set_description
|
||||||
|
self._parsed = None
|
||||||
|
|
||||||
|
def _ensure_parsed(self):
|
||||||
|
if not LARK_AVAILABLE:
|
||||||
|
return
|
||||||
|
if self._parsed is None:
|
||||||
|
self._parsed = parse_mvd(self.description)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> list[str]:
|
||||||
|
return self._get_description()
|
||||||
|
|
||||||
|
@description.setter
|
||||||
|
def description(self, new_description: list[str]):
|
||||||
|
self._set_description(new_description)
|
||||||
|
self._parsed = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def view_definitions(self):
|
||||||
|
self._ensure_parsed()
|
||||||
|
return ', '.join(' '.join(item.split()) for item in self._parsed.view_definitions) if self._parsed else None
|
||||||
|
|
||||||
|
@view_definitions.setter
|
||||||
|
def view_definitions(self, new_value: str):
|
||||||
|
self._update_keyword("ViewDefinition", new_value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def comments(self):
|
||||||
|
self._ensure_parsed()
|
||||||
|
comments = self._parsed.comments
|
||||||
|
comment_list = comments if isinstance(comments, list) else [comments] if comments else []
|
||||||
|
return AutoCommitList(
|
||||||
|
comment_list,
|
||||||
|
callback=lambda val: self._update_keyword("Comment", val),
|
||||||
|
formatter=lambda lst: ", ".join(str(i) for i in lst)
|
||||||
|
)
|
||||||
|
|
||||||
|
@comments.setter
|
||||||
|
def comments(self, new_value: str | list[str]):
|
||||||
|
if isinstance(new_value, list):
|
||||||
|
value = ", ".join(new_value)
|
||||||
|
else:
|
||||||
|
value = str(new_value)
|
||||||
|
self._update_keyword("Comment", value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def exchange_requirements(self):
|
||||||
|
self._ensure_parsed()
|
||||||
|
return self._parsed.exchange_requirements if self._parsed else None
|
||||||
|
|
||||||
|
@exchange_requirements.setter
|
||||||
|
def exchange_requirements(self, new_value: str):
|
||||||
|
self._update_keyword("ExchangeRequirement", new_value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def options(self):
|
||||||
|
self._ensure_parsed()
|
||||||
|
if isinstance(self._parsed.options, dict):
|
||||||
|
return DictionaryHandler(self._parsed.options, self, "Option")
|
||||||
|
return self._parsed.options if self._parsed else None
|
||||||
|
|
||||||
|
@options.setter
|
||||||
|
def options(self, new_value: str):
|
||||||
|
self._update_keyword("Option", new_value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def keywords(self):
|
||||||
|
self._ensure_parsed()
|
||||||
|
return self._parsed.keywords if self._parsed else set()
|
||||||
|
|
||||||
|
def _update_keyword(self, keyword: str, new_value: str):
|
||||||
|
updated = False
|
||||||
|
new_line = f"{keyword} [{new_value}]"
|
||||||
|
lines = []
|
||||||
|
for line in self.description:
|
||||||
|
if line.strip().startswith(f"{keyword} ["):
|
||||||
|
lines.append(new_line)
|
||||||
|
updated = True
|
||||||
|
else:
|
||||||
|
lines.append(line)
|
||||||
|
if not updated:
|
||||||
|
lines.append(new_line)
|
||||||
|
self.description = lines
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
self._ensure_parsed()
|
||||||
|
if hasattr(self._parsed, '_dynamic'):
|
||||||
|
name_lc = name.lower()
|
||||||
|
if name_lc in self._parsed._dynamic:
|
||||||
|
value, original_keyword = self._parsed._dynamic[name_lc]
|
||||||
|
return DictionaryHandler(value, self, original_keyword)
|
||||||
|
raise AttributeError(f"'MvdInfo' object has no attribute '{name}'")
|
||||||
|
|
||||||
|
def __dir__(self):
|
||||||
|
base = super().__dir__()
|
||||||
|
if self._parsed and hasattr(self._parsed, '_dynamic'):
|
||||||
|
return base + [kw for _, kw in self._parsed._dynamic.values()]
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
|
class DictionaryHandler(dict):
|
||||||
|
def __init__(self, initial_data, mvdinfo, keyword):
|
||||||
|
super().__init__()
|
||||||
|
self._mvdinfo = mvdinfo
|
||||||
|
self._keyword = keyword
|
||||||
|
for k, v in initial_data.items():
|
||||||
|
if isinstance(v, list):
|
||||||
|
super().__setitem__(k, AutoCommitList(v, self._commit))
|
||||||
|
else:
|
||||||
|
super().__setitem__(k, v)
|
||||||
|
|
||||||
|
def _commit(self):
|
||||||
|
new_value = "; ".join(
|
||||||
|
f"{k}: {', '.join(v) if isinstance(v, list) else v}"
|
||||||
|
for k, v in self.items()
|
||||||
|
)
|
||||||
|
self._mvdinfo._update_keyword(self._keyword, new_value)
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = AutoCommitList(value, self._commit)
|
||||||
|
super().__setitem__(key, value)
|
||||||
|
self._commit()
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
super().__delitem__(key)
|
||||||
|
self._commit()
|
||||||
|
|
||||||
|
|
||||||
|
class AutoCommitList(list):
|
||||||
|
"ensures keyword attributes are written back to ifcopenshell.file.header"
|
||||||
|
def __init__(self, iterable, callback, formatter=None):
|
||||||
|
super().__init__(iterable)
|
||||||
|
self._callback = callback
|
||||||
|
self._formatter = formatter
|
||||||
|
|
||||||
|
def _commit(self):
|
||||||
|
if self._formatter:
|
||||||
|
self._callback(self._formatter(self))
|
||||||
|
else:
|
||||||
|
self._callback()
|
||||||
|
|
||||||
|
def append(self, item):
|
||||||
|
super().append(item)
|
||||||
|
self._commit()
|
||||||
|
|
||||||
|
def extend(self, iterable):
|
||||||
|
super().extend(iterable)
|
||||||
|
self._commit()
|
||||||
|
|
||||||
|
def insert(self, index, item):
|
||||||
|
super().insert(index, item)
|
||||||
|
self._commit()
|
||||||
|
|
||||||
|
def remove(self, item):
|
||||||
|
super().remove(item)
|
||||||
|
self._commit()
|
||||||
|
|
||||||
|
def pop(self, index=-1):
|
||||||
|
item = super().pop(index)
|
||||||
|
self._commit()
|
||||||
|
return item
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
super().clear()
|
||||||
|
self._commit()
|
||||||
|
|
||||||
|
def __setitem__(self, index, value):
|
||||||
|
super().__setitem__(index, value)
|
||||||
|
self._commit()
|
||||||
|
|
||||||
|
def __delitem__(self, index):
|
||||||
|
super().__delitem__(index)
|
||||||
|
self._commit()
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]', 'Comment [Any]'),'2;1');
|
||||||
|
FILE_NAME('Header.ifc','2025-02-13T15:58:45',('tricott'),('Trimble Inc.'),'TrimBimToIFC rel. 4.0.2','Trimble Inc. - SketchUp - 2025.0','IFC4 model');
|
||||||
|
FILE_SCHEMA(('IFC4'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||||
|
#2=IFCORGANIZATION($,'',$,$,$);
|
||||||
|
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||||
|
#4=IFCAPPLICATION(#2,'v0.7.0-6c9e130ca','IfcOpenShell-v0.7.0-6c9e130ca','');
|
||||||
|
#5=IFCOWNERHISTORY(#3,#4,$,.NOTDEFINED.,$,#3,#4,1700419055);
|
||||||
|
#6=IFCDIRECTION((1.,0.,0.));
|
||||||
|
#7=IFCDIRECTION((0.,0.,1.));
|
||||||
|
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||||
|
#10=IFCDIRECTION((0.,1.));
|
||||||
|
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#10);
|
||||||
|
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||||
|
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
|
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
|
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
|
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||||
|
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||||
|
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||||
|
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||||
|
#20=IFCPROJECT('0iDmeiiLP3AOllitM2Favn',#5,'',$,$,$,$,(#11),#19);
|
||||||
|
#21=IFCSITE('3rg2jGkIH10RFhrQsGZKRk',#5,$,$,$,$,$,$,$,$,$,$,$,$);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]', 'ExchangeRequirement [Any]'),'2;1');
|
||||||
|
FILE_NAME('Header.ifc','2025-02-13T15:58:45',('tricott'),('Trimble Inc.'),'TrimBimToIFC rel. 4.0.2','Trimble Inc. - SketchUp - 2025.0','IFC4 model');
|
||||||
|
FILE_SCHEMA(('IFC4'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||||
|
#2=IFCORGANIZATION($,'',$,$,$);
|
||||||
|
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||||
|
#4=IFCAPPLICATION(#2,'v0.7.0-6c9e130ca','IfcOpenShell-v0.7.0-6c9e130ca','');
|
||||||
|
#5=IFCOWNERHISTORY(#3,#4,$,.NOTDEFINED.,$,#3,#4,1700419055);
|
||||||
|
#6=IFCDIRECTION((1.,0.,0.));
|
||||||
|
#7=IFCDIRECTION((0.,0.,1.));
|
||||||
|
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||||
|
#10=IFCDIRECTION((0.,1.));
|
||||||
|
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#10);
|
||||||
|
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||||
|
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
|
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
|
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
|
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||||
|
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||||
|
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||||
|
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||||
|
#20=IFCPROJECT('0iDmeiiLP3AOllitM2Favn',#5,'',$,$,$,$,(#11),#19);
|
||||||
|
#21=IFCSITE('3rg2jGkIH10RFhrQsGZKRk',#5,$,$,$,$,$,$,$,$,$,$,$,$);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]', 'Option [Any]'),'2;1');
|
||||||
|
FILE_NAME('Header.ifc','2025-02-13T15:58:45',('tricott'),('Trimble Inc.'),'TrimBimToIFC rel. 4.0.2','Trimble Inc. - SketchUp - 2025.0','IFC4 model');
|
||||||
|
FILE_SCHEMA(('IFC4'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||||
|
#2=IFCORGANIZATION($,'',$,$,$);
|
||||||
|
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||||
|
#4=IFCAPPLICATION(#2,'v0.7.0-6c9e130ca','IfcOpenShell-v0.7.0-6c9e130ca','');
|
||||||
|
#5=IFCOWNERHISTORY(#3,#4,$,.NOTDEFINED.,$,#3,#4,1700419055);
|
||||||
|
#6=IFCDIRECTION((1.,0.,0.));
|
||||||
|
#7=IFCDIRECTION((0.,0.,1.));
|
||||||
|
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||||
|
#10=IFCDIRECTION((0.,1.));
|
||||||
|
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#10);
|
||||||
|
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||||
|
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
|
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
|
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
|
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||||
|
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||||
|
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||||
|
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||||
|
#20=IFCPROJECT('0iDmeiiLP3AOllitM2Favn',#5,'',$,$,$,$,(#11),#19);
|
||||||
|
#21=IFCSITE('3rg2jGkIH10RFhrQsGZKRk',#5,$,$,$,$,$,$,$,$,$,$,$,$);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]', 'ViewDefinition [QuantityTakeOffAddOnView]', 'Option [ExcludedObjects: Stair, Ramp, Space; SplitLevel: On]', 'ExchangeRequirement [CustomRequirement: Value1, Value2]', 'Remark [SomeKey: SomeValue; AnotherKey: AnotherValue]', 'Comment [This is a free text comment, or a comma-separated list of items]'),'2;1');
|
||||||
|
FILE_NAME('Header example2.ifc', '2022-09-16T10:35:07', ('Evandro Alfieri'), ('buildingSMART Int.'), 'IFC Motor 1.0', 'Company - Application - 26.0.0.0', 'none');
|
||||||
|
FILE_SCHEMA(('IFC4X3_ADD2'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||||
|
#2=IFCORGANIZATION($,'',$,$,$);
|
||||||
|
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||||
|
#4=IFCAPPLICATION(#2,'v0.7.0-6c9e130ca','IfcOpenShell-v0.7.0-6c9e130ca','');
|
||||||
|
#5=IFCOWNERHISTORY(#3,#4,$,.NOTDEFINED.,$,#3,#4,1700419055);
|
||||||
|
#6=IFCDIRECTION((1.,0.,0.));
|
||||||
|
#7=IFCDIRECTION((0.,0.,1.));
|
||||||
|
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||||
|
#10=IFCDIRECTION((0.,1.));
|
||||||
|
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#10);
|
||||||
|
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||||
|
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
|
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
|
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
|
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||||
|
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||||
|
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||||
|
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||||
|
#20=IFCPROJECT('0iDmeiiLP3AOllitM2Favn',#5,'',$,$,$,$,(#11),#19);
|
||||||
|
#21=IFCSITE('3rg2jGkIH10RFhrQsGZKRk',#5,$,$,$,$,$,$,$,$,$,$,$,$);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1');
|
||||||
|
FILE_NAME('Header example2.ifc', '2022-09-16T10:35:07', ('Evandro Alfieri'), ('buildingSMART Int.'), 'IFC Motor 1.0', 'Company - Application - 26.0.0.0', 'none');
|
||||||
|
FILE_SCHEMA(('IFC4X3_ADD2'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||||
|
#2=IFCORGANIZATION($,'',$,$,$);
|
||||||
|
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||||
|
#4=IFCAPPLICATION(#2,'v0.7.0-6c9e130ca','IfcOpenShell-v0.7.0-6c9e130ca','');
|
||||||
|
#5=IFCOWNERHISTORY(#3,#4,$,.NOTDEFINED.,$,#3,#4,1700419055);
|
||||||
|
#6=IFCDIRECTION((1.,0.,0.));
|
||||||
|
#7=IFCDIRECTION((0.,0.,1.));
|
||||||
|
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||||
|
#10=IFCDIRECTION((0.,1.));
|
||||||
|
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#10);
|
||||||
|
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||||
|
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
|
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
|
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
|
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||||
|
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||||
|
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||||
|
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||||
|
#20=IFCPROJECT('0iDmeiiLP3AOllitM2Favn',#5,'',$,$,$,$,(#11),#19);
|
||||||
|
#21=IFCSITE('3rg2jGkIH10RFhrQsGZKRk',#5,$,$,$,$,$,$,$,$,$,$,$,$);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION(('ViewDefinition [CoordinationView_V2.0]', 'ViewDefinition[SpaceBoundaryAddonView]'),'2;1');
|
||||||
|
FILE_NAME('Header example2.ifc', '2022-09-16T10:35:07', ('Evandro Alfieri'), ('buildingSMART Int.'), 'IFC Motor 1.0', 'Company - Application - 26.0.0.0', 'none');
|
||||||
|
FILE_SCHEMA(('IFC2X3'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||||
|
#2=IFCORGANIZATION($,'',$,$,$);
|
||||||
|
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||||
|
#4=IFCAPPLICATION(#2,'v0.7.0-6c9e130ca','IfcOpenShell-v0.7.0-6c9e130ca','');
|
||||||
|
#5=IFCOWNERHISTORY(#3,#4,$,.NOTDEFINED.,$,#3,#4,1700419055);
|
||||||
|
#6=IFCDIRECTION((1.,0.,0.));
|
||||||
|
#7=IFCDIRECTION((0.,0.,1.));
|
||||||
|
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||||
|
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||||
|
#10=IFCDIRECTION((0.,1.));
|
||||||
|
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#10);
|
||||||
|
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||||
|
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||||
|
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||||
|
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||||
|
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||||
|
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||||
|
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||||
|
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||||
|
#20=IFCPROJECT('0iDmeiiLP3AOllitM2Favn',#5,'',$,$,$,$,(#11),#19);
|
||||||
|
#21=IFCSITE('3rg2jGkIH10RFhrQsGZKRk',#5,$,$,$,$,$,$,$,$,$,$,$,$);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
import ifcopenshell
|
||||||
|
from ifcopenshell.util import mvd_info
|
||||||
|
from ifcopenshell.util.mvd_info import parse_mvd
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def load_fixture():
|
||||||
|
base_dir = os.path.join(os.path.dirname(__file__), "fixtures", "mvd_parsing")
|
||||||
|
def _load(filename):
|
||||||
|
return ifcopenshell.open(os.path.join(base_dir, filename))
|
||||||
|
return _load
|
||||||
|
|
||||||
|
|
||||||
|
class TestViewDefinition:
|
||||||
|
def test_single_view(self, load_fixture):
|
||||||
|
f = load_fixture("passing_header.ifc")
|
||||||
|
assert f.mvd.view_definitions == "Alignment-basedView"
|
||||||
|
|
||||||
|
def test_multiple_views(self, load_fixture):
|
||||||
|
f = load_fixture("two_views.ifc")
|
||||||
|
assert f.mvd.view_definitions == 'CoordinationView_V2.0, SpaceBoundaryAddonView'
|
||||||
|
|
||||||
|
|
||||||
|
class TestExchangeRequirements:
|
||||||
|
def test_parsing(self, load_fixture):
|
||||||
|
f = load_fixture("contains_exchange_requirement.ifc")
|
||||||
|
parsed = parse_mvd(f.mvd.description)
|
||||||
|
assert parsed.exchange_requirements == 'Any'
|
||||||
|
|
||||||
|
def test_access_and_modification(self, load_fixture):
|
||||||
|
f = load_fixture("contains_exchange_requirement.ifc")
|
||||||
|
f.header.file_description.description = (
|
||||||
|
'ViewDefinition [Alignment-basedView]',
|
||||||
|
'ExchangeRequirement [SomethingElse]'
|
||||||
|
)
|
||||||
|
assert f.mvd.exchange_requirements == 'SomethingElse'
|
||||||
|
f.mvd.view_definitions = 'CoordinationView_V2.0'
|
||||||
|
assert f.mvd.view_definitions == 'CoordinationView_V2.0'
|
||||||
|
|
||||||
|
|
||||||
|
class TestComments:
|
||||||
|
def test_read_and_append(self, load_fixture):
|
||||||
|
f = load_fixture("contains_comment.ifc")
|
||||||
|
assert f.mvd.comments == ['Any']
|
||||||
|
f.mvd.comments = ['SomethingElse']
|
||||||
|
assert f.mvd.comments == ['SomethingElse']
|
||||||
|
f.mvd.comments.append('AnotherComment')
|
||||||
|
assert f.mvd.comments == ['SomethingElse', ' AnotherComment']
|
||||||
|
assert f.mvd.description[1] == 'Comment [SomethingElse, AnotherComment]'
|
||||||
|
|
||||||
|
def test_comment_list_modifications(self, load_fixture):
|
||||||
|
f = load_fixture("contains_comment.ifc")
|
||||||
|
f.mvd.comments = ''
|
||||||
|
f.mvd.comments.append('OnlyOne')
|
||||||
|
assert 'OnlyOne' in f.mvd.comments
|
||||||
|
|
||||||
|
f.mvd.comments.insert(0, 'FirstOne')
|
||||||
|
f.mvd.comments[0] == 'FirstOne'
|
||||||
|
|
||||||
|
f.mvd.comments.pop()
|
||||||
|
assert f.mvd.comments[0] == 'FirstOne'
|
||||||
|
|
||||||
|
del f.mvd.comments[0]
|
||||||
|
assert not f.mvd.comments
|
||||||
|
|
||||||
|
|
||||||
|
class TestOptions:
|
||||||
|
def test_string_options(self, load_fixture):
|
||||||
|
f = load_fixture("contains_options.ifc")
|
||||||
|
assert f.mvd.options == 'Any'
|
||||||
|
assert 'options' in f.mvd.keywords
|
||||||
|
|
||||||
|
|
||||||
|
class TestDynamicFields:
|
||||||
|
def test_options_modifications(self, load_fixture):
|
||||||
|
f = load_fixture("dynamic_fields.ifc")
|
||||||
|
|
||||||
|
f.mvd.options['ExcludedObjects'].append('Chair')
|
||||||
|
f.mvd.options['SplitLevel'] = 'Off'
|
||||||
|
f.mvd.options['OtherAttr'] = 'SomeValue'
|
||||||
|
|
||||||
|
assert f.mvd.description[2].startswith('Option [')
|
||||||
|
assert 'OtherAttr: SomeValue' in f.mvd.description[2]
|
||||||
|
assert f.mvd.description == f.header.file_description.description
|
||||||
|
|
||||||
|
def test_remark_editing(self, load_fixture):
|
||||||
|
f = load_fixture("dynamic_fields.ifc")
|
||||||
|
assert f.mvd.remark == {'SomeKey': 'SomeValue', 'AnotherKey': 'AnotherValue'}
|
||||||
|
f.mvd.remark['AnotherKey'] = 'SometingElse'
|
||||||
|
f.mvd.remark['IncludedObjects'] = ['Floor', 'Roof']
|
||||||
|
assert f.mvd.remark['AnotherKey'] == 'SometingElse'
|
||||||
|
assert f.mvd.remark['IncludedObjects'] == ['Floor', 'Roof']
|
||||||
|
assert 'remark' in f.mvd.keywords
|
||||||
|
|
||||||
|
def test_custom_dict_behavior(self, load_fixture):
|
||||||
|
f = load_fixture("dynamic_fields.ifc")
|
||||||
|
|
||||||
|
# delete
|
||||||
|
del f.mvd.options['SplitLevel']
|
||||||
|
assert not f.mvd.options.get('SplitLevel')
|
||||||
|
|
||||||
|
# keys, values, items
|
||||||
|
assert set(f.mvd.remark.keys()) == {'SomeKey', 'AnotherKey'}
|
||||||
|
assert 'SomeValue' in f.mvd.remark.values()
|
||||||
|
assert ('SomeKey', 'SomeValue') in f.mvd.remark.items()
|
||||||
|
|
||||||
|
# containment
|
||||||
|
assert 'SomeKey' in f.mvd.remark
|
||||||
|
assert 'MissingKey' not in f.mvd.remark
|
||||||
|
|
||||||
|
|
||||||
|
class TestKeywords:
|
||||||
|
@pytest.mark.parametrize("filename, expected_keywords", [
|
||||||
|
("contains_comment.ifc", {"view_definitions", "comments"}),
|
||||||
|
("contains_exchange_requirement.ifc", {"view_definitions", "exchange_requirements"}),
|
||||||
|
("contains_options.ifc", {"view_definitions", "options"}),
|
||||||
|
("dynamic_fields.ifc", {"view_definitions", "exchange_requirements", "comments", "remark"})
|
||||||
|
])
|
||||||
|
def test_keywords_present(self, load_fixture, filename, expected_keywords):
|
||||||
|
f = load_fixture(filename)
|
||||||
|
assert f.mvd.keywords == expected_keywords
|
||||||
|
|
||||||
|
|
||||||
|
class TestFallbackBehavior:
|
||||||
|
def test_parse_mvd_fallback(self, monkeypatch):
|
||||||
|
monkeypatch.setattr(mvd_info, "LARK_AVAILABLE", False)
|
||||||
|
mvd = mvd_info.MvdInfo(
|
||||||
|
get_description=lambda: ["ViewDefinition [ShouldNotParse]"],
|
||||||
|
set_description=lambda d: None
|
||||||
|
)
|
||||||
|
assert mvd.view_definitions is None
|
||||||
|
assert mvd.keywords == set()
|
||||||
Reference in New Issue
Block a user