bonsai - bcf v3 document references #2790

This commit is contained in:
Andrej730
2024-08-15 17:50:18 +05:00
parent dc6fdc2a98
commit beb7f75368
4 changed files with 148 additions and 20 deletions
+104 -18
View File
@@ -18,6 +18,8 @@
import os
import bcf.v3
import bcf.v3.bcfxml
import bcf.v3.document
import bcf.v3.model
import bcf.v3.topic
import bpy
@@ -714,30 +716,70 @@ class AddBcfDocumentReference(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_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"}
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
is_external = "://" in props.document_reference
filename = Path(props.document_reference).name if os.path.exists(props.document_reference) else None
document_path = Path(props.document_reference)
document_bytes, filename = None, None
if document_path.is_file():
filename = document_path.name
document_bytes = document_path.read_bytes()
if filename:
with open(props.document_reference, "rb") as f:
topic.document_references[filename] = f.read()
document_bytes = f.read()
document_reference = bcf.v2.model.TopicDocumentReference(
referenced_document=props.document_reference if is_external else filename,
description=props.document_reference_description or None,
guid=str(uuid.uuid4()),
is_external=is_external,
)
document_references = tool.Bcf.get_topic_document_references(topic)
if bcf_v2:
assert isinstance(topic, bcf.v2.topic.TopicHandler)
if document_bytes and filename:
topic.document_references[filename] = document_bytes
assert tool.Bcf.is_list_of(document_references, bcf.v2.model.TopicDocumentReference)
document_reference = bcf.v2.model.TopicDocumentReference(
referenced_document=props.document_reference if is_external else filename,
description=props.document_reference_description or None,
guid=str(uuid.uuid4()),
is_external=is_external,
)
document_references.append(document_reference)
else:
document_guid = None
if not is_external:
assert filename and document_bytes
assert isinstance(bcfxml, bcf.v3.bcfxml.BcfXml)
bcf_docs = bcfxml.documents
topic.topic.document_reference.append(document_reference)
if bcf_docs:
doc_definition = bcf_docs.definition
else:
doc_definition = bcf.v3.model.DocumentInfo()
bcf_docs = bcf.v3.document.DocumentsHandler(doc_definition)
bcfxml._documents = bcf_docs
bcf_docs.documents[filename] = document_bytes
document_guid = str(uuid.uuid4())
document = bcf.v3.model.Document(
filename=filename, description=props.document_description, guid=document_guid
)
doc_definition_docs = doc_definition.documents
if not doc_definition_docs:
doc_definition.documents = (doc_definition_docs := bcf.v3.model.DocumentInfoDocuments())
doc_definition_docs.document.append(document)
assert tool.Bcf.is_list_of(document_references, bcf.v3.model.DocumentReference)
document_reference = bcf.v3.model.DocumentReference(
document_guid=document_guid,
url=props.document_reference if is_external else None,
description=props.document_reference_description or None,
guid=str(uuid.uuid4()),
)
document_references.append(document_reference)
tool.Bcf.set_topic_document_references(topic, document_references)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.document_reference = ""
@@ -877,15 +919,59 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_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"}
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.topic.document_reference[self.index]
document_references = tool.Bcf.get_topic_document_references(topic)
topic_index: int = self.index
document_reference = document_references[topic_index]
# Remove document contents.
if bcf_v2:
assert isinstance(topic, bcf.v2.topic.TopicHandler)
assert isinstance(document_reference, bcf.v2.model.TopicDocumentReference)
if not document_reference.is_external:
ref_document = document_reference.referenced_document
assert ref_document
del topic.document_references[ref_document]
else:
assert isinstance(document_reference, bcf.v3.model.DocumentReference)
document_guid = document_reference.document_guid
# For bcf v3 documents are stored in bcfxml, not in the topic.
if document_guid:
assert isinstance(bcfxml, bcf.v3.bcfxml.BcfXml)
# As there's no bcfxml document manager ui yet,
# we remove document if it's not referenced by any topic.
present_in_other_topics = False
for topic_ in bcfxml.topics.values():
if topic_ == topic:
continue
document_references = tool.Bcf.get_topic_document_references(topic_)
assert tool.Bcf.is_list_of(document_references, bcf.v3.model.DocumentReference)
for ref_ in document_references:
if ref_.document_guid == document_guid:
present_in_other_topics = True
break
if present_in_other_topics:
break
if not present_in_other_topics:
doc_handler = bcfxml.documents
assert doc_handler
docs = doc_handler.definition.documents
assert docs
doc = next(d for d in docs.document if d.guid == document_guid)
del doc_handler.documents[doc.filename]
docs.document.remove(doc)
# Remove document reference.
del document_references[topic_index]
tool.Bcf.set_topic_document_references(topic, document_references)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
return {"FINISHED"}
+1
View File
@@ -210,6 +210,7 @@ class BCFProperties(PropertyGroup):
bim_snippet_schema: StringProperty(default="", name="Schema")
document_reference: StringProperty(default="", name="Referenced Document")
document_reference_description: StringProperty(default="", name="Description")
document_description: StringProperty(default="", name="Document Description")
related_topic: StringProperty(name="Related Topic")
comment: StringProperty(default="", name="Comment")
has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False)
+12 -1
View File
@@ -116,6 +116,10 @@ class BIM_PT_bcf_metadata(Panel):
bl_parent_id = "BIM_PT_bcf"
def draw(self, context):
# TODO: in bcf v3 documents can be added to the bcf
# without adding them to specific topic.
# Currently we just handle it the same way as in v2
# documents are accessed and managed from topics.
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
@@ -127,6 +131,8 @@ class BIM_PT_bcf_metadata(Panel):
if not bcfxml or props.active_topic_index >= len(props.topics):
layout.label(text="No BCF project is loaded")
return
bcf_verison = bcfxml.version.version_id or ""
bcf_v3 = bcf_verison.startswith("3")
topic = props.active_topic
bcf_topic = bcfxml.topics[topic.name]
@@ -239,7 +245,12 @@ class BIM_PT_bcf_metadata(Panel):
row.prop(props, "document_reference")
row.operator("bim.select_bcf_document_reference", icon="FILE_FOLDER", text="")
row = layout.row()
row.prop(props, "document_reference_description")
row.prop(
props, "document_reference_description", text="Reference Description" if bcf_v3 else "Document Description"
)
if bcf_v3:
row = layout.row()
row.prop(props, "document_description")
row = layout.row()
row.operator("bim.add_bcf_document_reference")
+31 -1
View File
@@ -26,7 +26,10 @@ import bcf.v2.topic
import bcf.v3.model
import bcf.v3.topic
import bcf.agnostic.topic
from typing import Any, Union, Optional
from typing import Any, Union, TypeVar, TypeGuard
T = TypeVar("T")
class Bcf(bonsai.core.tool.Bcf):
@@ -34,6 +37,13 @@ class Bcf(bonsai.core.tool.Bcf):
def get_path(cls) -> str:
return bpy.context.scene.BCFProperties.bcf_file
@classmethod
def is_list_of(cls, a: list[Any], t: type[T]) -> TypeGuard[list[T]]:
"""Narrow type for a list assuming list is not mixing types."""
if not a:
return True
return isinstance(a[0], t)
# BCF version agnostic API.
## topic
@classmethod
@@ -47,6 +57,26 @@ class Bcf(bonsai.core.tool.Bcf):
document_references = document_references.document_reference if document_references else []
return document_references
@classmethod
def set_topic_document_references(
cls,
topic: bcf.agnostic.topic.TopicHandler,
document_references: Union[list[bcf.v2.model.TopicDocumentReference], list[bcf.v3.model.DocumentReference]],
) -> None:
topic_ = topic.topic
if isinstance(topic_, bcf.v2.model.Topic):
assert cls.is_list_of(document_references, bcf.v2.model.TopicDocumentReference)
topic_.document_reference = document_references
else:
assert cls.is_list_of(document_references, bcf.v3.model.DocumentReference)
topic_document_references = topic_.document_references
if topic_document_references is None:
if not document_references:
return
topic_.document_references = (topic_document_references := bcf.v3.model.TopicDocumentReferences())
return
topic_document_references.document_reference = document_references
@classmethod
def get_topic_header_files(
cls, topic: bcf.agnostic.topic.TopicHandler