mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 12:38:03 +00:00
bcf v3 & bonsai bcf v3 - support adding/removing header files #2790
Also removed unnecessary read in AddBcfDocumentReference.
This commit is contained in:
@@ -63,6 +63,11 @@ class TopicHandler:
|
|||||||
"""Return the header of the topic."""
|
"""Return the header of the topic."""
|
||||||
return self.markup.header if self.markup else None
|
return self.markup.header if self.markup else None
|
||||||
|
|
||||||
|
@header.setter
|
||||||
|
def header(self, header: mdl.Header) -> None:
|
||||||
|
"""Set the header of the topic."""
|
||||||
|
self.markup.header = header
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def comments(self) -> list[mdl.Comment]:
|
def comments(self) -> list[mdl.Comment]:
|
||||||
"""Return the comments of the topic."""
|
"""Return the comments of the topic."""
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class TopicHandler:
|
|||||||
) -> None:
|
) -> None:
|
||||||
self._markup: Optional[mdl.Markup] = None
|
self._markup: Optional[mdl.Markup] = None
|
||||||
self._viewpoints: Optional[dict[str, VisualizationInfoHandler]] = None
|
self._viewpoints: Optional[dict[str, VisualizationInfoHandler]] = None
|
||||||
|
self._reference_files: Optional[dict[str, bytes]] = None
|
||||||
self._bim_snippet: Optional[bytes] = None
|
self._bim_snippet: Optional[bytes] = None
|
||||||
self._xml_handler = xml_handler or XmlParserSerializer()
|
self._xml_handler = xml_handler or XmlParserSerializer()
|
||||||
self._topic_dir = topic_dir
|
self._topic_dir = topic_dir
|
||||||
@@ -60,6 +61,11 @@ class TopicHandler:
|
|||||||
"""Return the header of the topic."""
|
"""Return the header of the topic."""
|
||||||
return self.markup.header
|
return self.markup.header
|
||||||
|
|
||||||
|
@header.setter
|
||||||
|
def header(self, header: mdl.Header) -> None:
|
||||||
|
"""Set the header of the topic."""
|
||||||
|
self.markup.header = header
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def comments(self) -> list[mdl.Comment]:
|
def comments(self) -> list[mdl.Comment]:
|
||||||
"""Return the comments of the topic."""
|
"""Return the comments of the topic."""
|
||||||
@@ -103,6 +109,27 @@ class TopicHandler:
|
|||||||
return bim_snippet_path.read_bytes()
|
return bim_snippet_path.read_bytes()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reference_files(self) -> dict[str, bytes]:
|
||||||
|
if self._reference_files is not None:
|
||||||
|
return self._reference_files
|
||||||
|
|
||||||
|
self._reference_files = {}
|
||||||
|
if not self.header:
|
||||||
|
return self._reference_files
|
||||||
|
|
||||||
|
if not self.header.files:
|
||||||
|
return self._reference_files
|
||||||
|
|
||||||
|
for ref in self.header.files.file:
|
||||||
|
if ref.is_external:
|
||||||
|
continue
|
||||||
|
real_path = self._topic_dir
|
||||||
|
for path_part in ref.reference.split("/"):
|
||||||
|
real_path = real_path.parent if path_part == ".." else real_path.joinpath(path_part)
|
||||||
|
self._reference_files[ref.reference] = real_path.read_bytes()
|
||||||
|
return self._reference_files
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_new(
|
def create_new(
|
||||||
cls,
|
cls,
|
||||||
@@ -154,6 +181,7 @@ class TopicHandler:
|
|||||||
self._save_xml(destination_zip, self._markup, "markup.bcf")
|
self._save_xml(destination_zip, self._markup, "markup.bcf")
|
||||||
self._save_viewpoints(destination_zip, topic_dir)
|
self._save_viewpoints(destination_zip, topic_dir)
|
||||||
self._save_bim_snippet(destination_zip)
|
self._save_bim_snippet(destination_zip)
|
||||||
|
self._save_reference_files(destination_zip)
|
||||||
|
|
||||||
def _save_viewpoints(self, destination_zip: ZipFileInterface, topic_dir: str) -> None:
|
def _save_viewpoints(self, destination_zip: ZipFileInterface, topic_dir: str) -> None:
|
||||||
if not self.topic.viewpoints or not (viewpoints := self.topic.viewpoints.view_point):
|
if not self.topic.viewpoints or not (viewpoints := self.topic.viewpoints.view_point):
|
||||||
@@ -174,6 +202,19 @@ class TopicHandler:
|
|||||||
if self.bim_snippet:
|
if self.bim_snippet:
|
||||||
destination_zip.writestr(f"{self.topic.guid}/{ref_filename}", self.bim_snippet)
|
destination_zip.writestr(f"{self.topic.guid}/{ref_filename}", self.bim_snippet)
|
||||||
|
|
||||||
|
def _save_reference_files(self, destination_zip: ZipFileInterface) -> None:
|
||||||
|
if not self.header:
|
||||||
|
return
|
||||||
|
if not self.header.files:
|
||||||
|
return
|
||||||
|
for ref in self.header.files.file:
|
||||||
|
if ref.is_external or not ref.reference:
|
||||||
|
continue
|
||||||
|
real_path = self._topic_dir
|
||||||
|
for path_part in ref.reference.split("/"):
|
||||||
|
real_path = real_path.parent if path_part == ".." else real_path.joinpath(path_part)
|
||||||
|
destination_zip.writestr(real_path.at, self.reference_files[ref.reference])
|
||||||
|
|
||||||
def add_viewpoint(self, element: entity_instance) -> VisualizationInfoHandler:
|
def add_viewpoint(self, element: entity_instance) -> VisualizationInfoHandler:
|
||||||
"""
|
"""
|
||||||
Add a viewpoint tergeting an IFC element to the topic.
|
Add a viewpoint tergeting an IFC element to the topic.
|
||||||
|
|||||||
@@ -475,31 +475,46 @@ class AddBcfHeaderFile(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
assert bcfxml
|
assert bcfxml
|
||||||
|
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
|
||||||
if not (version := (bcfxml.version.version_id or "")).startswith("2"):
|
|
||||||
self.report({"INFO"}, f"BCF {version} is not yet supported: {self.bl_rna.bl_idname}.")
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
props = context.scene.BCFProperties
|
props = context.scene.BCFProperties
|
||||||
blender_topic = props.active_topic
|
blender_topic = props.active_topic
|
||||||
topic = bcfxml.topics[blender_topic.name]
|
topic = bcfxml.topics[blender_topic.name]
|
||||||
|
|
||||||
is_external = "://" in props.file_reference
|
is_external = "://" in props.file_reference
|
||||||
filename = Path(props.file_reference).name if os.path.exists(props.file_reference) else None
|
filepath = Path(props.file_reference)
|
||||||
|
file_bytes, filename = None, None
|
||||||
|
if filepath.is_file():
|
||||||
|
filename = filepath.name
|
||||||
|
file_bytes = filepath.read_bytes()
|
||||||
|
|
||||||
if filename:
|
header_files = tool.Bcf.get_topic_header_files(topic)
|
||||||
with open(props.file_reference, "r") as f:
|
if filename and file_bytes:
|
||||||
topic.reference_files[filename] = f.read()
|
topic.reference_files[filename] = file_bytes
|
||||||
|
if bcf_v2:
|
||||||
|
header_file = bcf.v2.model.HeaderFile(
|
||||||
|
filename=filename,
|
||||||
|
date=XmlDateTime.now(),
|
||||||
|
reference=props.file_reference if is_external else filename,
|
||||||
|
ifc_project=props.file_ifc_project,
|
||||||
|
ifc_spatial_structure_element=props.file_ifc_spatial_structure_element,
|
||||||
|
is_external=is_external,
|
||||||
|
)
|
||||||
|
assert tool.Bcf.is_list_of(header_files, bcf.v2.model.HeaderFile)
|
||||||
|
header_files.append(header_file)
|
||||||
|
else:
|
||||||
|
header_file = bcf.v3.model.File(
|
||||||
|
filename=filename,
|
||||||
|
date=XmlDateTime.now(),
|
||||||
|
reference=props.file_reference if is_external else filename,
|
||||||
|
ifc_project=props.file_ifc_project,
|
||||||
|
ifc_spatial_structure_element=props.file_ifc_spatial_structure_element,
|
||||||
|
is_external=is_external,
|
||||||
|
)
|
||||||
|
assert tool.Bcf.is_list_of(header_files, bcf.v3.model.File)
|
||||||
|
header_files.append(header_file)
|
||||||
|
|
||||||
header_file = bcf.v2.model.HeaderFile(
|
tool.Bcf.set_topic_header_files(topic, header_files)
|
||||||
filename=filename,
|
|
||||||
date=XmlDateTime.now(),
|
|
||||||
reference=props.file_reference if is_external else Path(props.file_reference).name,
|
|
||||||
ifc_project=props.file_ifc_project,
|
|
||||||
ifc_spatial_structure_element=props.file_ifc_spatial_structure_element,
|
|
||||||
is_external=is_external,
|
|
||||||
)
|
|
||||||
topic.header.file.append(header_file)
|
|
||||||
|
|
||||||
props.refresh_topic(context)
|
props.refresh_topic(context)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@@ -652,14 +667,12 @@ class RemoveBcfFile(bpy.types.Operator):
|
|||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
assert bcfxml
|
assert bcfxml
|
||||||
|
|
||||||
if not (version := (bcfxml.version.version_id or "")).startswith("2"):
|
|
||||||
self.report({"INFO"}, f"BCF {version} is not yet supported: {self.bl_rna.bl_idname}.")
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
props = context.scene.BCFProperties
|
props = context.scene.BCFProperties
|
||||||
blender_topic = props.active_topic
|
blender_topic = props.active_topic
|
||||||
topic = bcfxml.topics[blender_topic.name]
|
topic = bcfxml.topics[blender_topic.name]
|
||||||
del topic.header.file[self.index]
|
header_files = tool.Bcf.get_topic_header_files(topic)
|
||||||
|
del header_files[self.index]
|
||||||
|
tool.Bcf.set_topic_header_files(topic, header_files)
|
||||||
props.refresh_topic(context)
|
props.refresh_topic(context)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -734,10 +747,6 @@ class AddBcfDocumentReference(bpy.types.Operator):
|
|||||||
filename = document_path.name
|
filename = document_path.name
|
||||||
document_bytes = document_path.read_bytes()
|
document_bytes = document_path.read_bytes()
|
||||||
|
|
||||||
if filename:
|
|
||||||
with open(props.document_reference, "rb") as f:
|
|
||||||
document_bytes = f.read()
|
|
||||||
|
|
||||||
document_references = tool.Bcf.get_topic_document_references(topic)
|
document_references = tool.Bcf.get_topic_document_references(topic)
|
||||||
if bcf_v2:
|
if bcf_v2:
|
||||||
assert isinstance(topic, bcf.v2.topic.TopicHandler)
|
assert isinstance(topic, bcf.v2.topic.TopicHandler)
|
||||||
|
|||||||
@@ -112,6 +112,37 @@ class Bcf(bonsai.core.tool.Bcf):
|
|||||||
header_files = header_files.file if header_files else []
|
header_files = header_files.file if header_files else []
|
||||||
return header_files
|
return header_files
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_topic_header_files(
|
||||||
|
cls,
|
||||||
|
topic: bcf.agnostic.topic.TopicHandler,
|
||||||
|
files: Union[list[bcf.v2.model.HeaderFile], list[bcf.v3.model.File]],
|
||||||
|
) -> None:
|
||||||
|
if isinstance(topic, bcf.v2.topic.TopicHandler):
|
||||||
|
header = topic.header
|
||||||
|
if not header:
|
||||||
|
if not files:
|
||||||
|
return
|
||||||
|
topic.header = (header := bcf.v2.model.Header())
|
||||||
|
if not header.file:
|
||||||
|
if not files:
|
||||||
|
return
|
||||||
|
assert cls.is_list_of(files, bcf.v2.model.HeaderFile)
|
||||||
|
header.file = files
|
||||||
|
else:
|
||||||
|
header = topic.header
|
||||||
|
if not header:
|
||||||
|
if not files:
|
||||||
|
return
|
||||||
|
header = bcf.v3.model.Header()
|
||||||
|
header_files = header.files
|
||||||
|
if not header_files:
|
||||||
|
if not files:
|
||||||
|
return
|
||||||
|
header.files = (header_files := bcf.v3.model.HeaderFiles())
|
||||||
|
assert cls.is_list_of(files, bcf.v3.model.File)
|
||||||
|
header_files.file = files
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_topic_labels(cls, topic: bcf.agnostic.topic.TopicHandler) -> list[str]:
|
def get_topic_labels(cls, topic: bcf.agnostic.topic.TopicHandler) -> list[str]:
|
||||||
if isinstance(topic, bcf.v2.topic.TopicHandler):
|
if isinstance(topic, bcf.v2.topic.TopicHandler):
|
||||||
|
|||||||
Reference in New Issue
Block a user