[BCF] New BCF XML handler with better performances (#2355)

BREAKING CHANGE: incompatible methods and objects with previous version.

closes #2321
This commit is contained in:
Andrea Ghensi
2022-11-08 22:54:23 +01:00
committed by GitHub
parent d97594df96
commit 2cbc7a10ab
50 changed files with 5591 additions and 2340 deletions
+1
View File
@@ -0,0 +1 @@
"""BCF tests."""
+8
View File
@@ -0,0 +1,8 @@
import pytest
from bcf.xml_parser import XmlParserSerializer
@pytest.fixture(scope="session")
def xml_handler() -> XmlParserSerializer:
return XmlParserSerializer()
Binary file not shown.
View File
+122
View File
@@ -0,0 +1,122 @@
"""BCF XML tests."""
import uuid
from pathlib import Path
from tempfile import TemporaryDirectory
import pytest
import bcf.v2.model as mdl
from bcf.v2.bcfxml import BcfXml
from bcf.v2.topic import TopicHandler
from bcf.v2.visinfo import (
VisualizationInfoHandler,
build_camera_from_vectors,
build_components,
)
from bcf.xml_parser import XmlParserSerializer
@pytest.fixture()
def build_sample(xml_handler: XmlParserSerializer) -> tuple[BcfXml, TopicHandler]:
bcf = BcfXml.create_new("Test project", xml_handler=xml_handler)
orig_th = bcf.add_topic("Test topic", "Test message", "Test author", "Test type")
return bcf, orig_th
def test_bcf_roundtrip(xml_handler, build_sample) -> None:
"""Saving and loading a bcf xml project returns the same objects."""
bcf, orig_th = build_sample
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path)
with BcfXml.load(file_path, xml_handler=xml_handler) as parsed:
assert parsed == bcf
parsed_th = parsed.topics[orig_th.guid]
assert parsed_th == orig_th
def test_bcf_edit_saveas(xml_handler, build_sample) -> None:
"""Saving and loading a bcf xml project returns the same objects."""
bcf, orig_th = build_sample
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path)
with BcfXml.load(file_path, xml_handler=xml_handler) as parsed:
for th in parsed.topics.values():
th.topic.title = "New Topic Title"
modified_path = Path(tmp_dir) / "edited.bcf"
parsed.save(modified_path)
with BcfXml.load(modified_path, xml_handler=xml_handler) as modified_parsed:
assert modified_parsed == bcf
parsed_th = modified_parsed.topics[orig_th.guid]
assert parsed_th.markup != orig_th.markup
assert parsed_th.markup == parsed.topics[orig_th.guid].markup
assert parsed_th.viewpoints == orig_th.viewpoints
assert parsed_th.bim_snippet == orig_th.bim_snippet
def test_bcf_edit(xml_handler, build_sample) -> None:
"""Saving and loading a bcf xml project returns the same objects."""
bcf, orig_th = build_sample
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path)
with BcfXml.load(file_path, xml_handler=xml_handler) as parsed:
for th in parsed.topics.values():
th.topic.title = "New Topic Title"
parsed.save()
with BcfXml.load(file_path, xml_handler=xml_handler) as modified_parsed:
assert modified_parsed == bcf
assert len(modified_parsed.topics) == 1
parsed_th = modified_parsed.topics[orig_th.guid]
assert parsed_th.markup != orig_th.markup
assert parsed_th.markup == parsed.topics[orig_th.guid].markup
assert parsed_th.viewpoints == orig_th.viewpoints
assert parsed_th.bim_snippet == orig_th.bim_snippet
def test_save_no_filename(build_sample) -> None:
bcf, _ = build_sample
with pytest.raises(ValueError):
bcf.save()
def test_load_no_filename() -> None:
with pytest.raises(ValueError):
BcfXml.load("")
def test_save_keep_open(build_sample) -> None:
bcf, _ = build_sample
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path, keep_open=True)
assert bcf._zip_file is not None
bcf._zip_file.close()
def test_massive_bcf(xml_handler) -> None:
bcf = BcfXml.create_new("Test project", xml_handler=xml_handler)
for i in range(100):
th = bcf.add_topic(f"Topic {i:04}", f"Message {i:04}", "Test author", "Test type")
vi = mdl.VisualizationInfo(
guid=str(uuid.uuid4()),
components=build_components(str(uuid.uuid4())),
perspective_camera=build_camera_from_vectors([i, 0, 0], [0, 1, 0], [0, 0, 1]),
)
vh = VisualizationInfoHandler(visualization_info=vi, xml_handler=xml_handler)
th.add_visinfo_handler(vh)
assert len(bcf.topics) == 100
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path)
def test_equality_with_wrong_object() -> None:
with pytest.raises(TypeError):
build_sample[0] == "Wrong object"
def test_topic_equality_with_wrong_object() -> None:
with pytest.raises(TypeError):
build_sample[1] == "Wrong object"
+365
View File
@@ -0,0 +1,365 @@
import json
import os
import zipfile
from pathlib import Path
from xsdata.models.datatype import XmlDateTime
import bcf.v2.model as mdl
from bcf.v2.bcfxml import BcfXml
from bcf.v2.topic import TopicHandler
def test_maximum_information() -> None:
"""
All the info in a BCF is parsed correctly.
Uses sample file from https://github.com/buildingSMART/BCF-XML/
"""
bcf_path = Path(__file__).parent / "MaximumInformation.bcf"
with BcfXml.load(bcf_path) as bcf:
assert_everything_in_place(bcf)
def test_save_maximum_information() -> None:
base_dir = Path(__file__).parent
bcf_path = base_dir / "MaximumInformation.bcf"
target_path = base_dir / "MaximumInformationSaved.bcf"
with BcfXml.load(bcf_path) as bcf:
bcf.save(target_path)
assert target_path.exists()
with BcfXml.load(target_path) as bcf2:
assert_everything_in_place(bcf2)
expected_files = [
"bcf.version",
"project.bcfp",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/markup.bcf",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/bitmap.png",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/tux.png",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/JsonElement.json",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/Viewpoint_4ab7514b-b216-4d56-98d2-45cf8500ff5a.bcfv",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/Viewpoint_9a4a1878-ecbd-4916-83a8-dad82e560231.bcfv",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/Viewpoint_fc4019d7-365e-47f3-b6d0-b39fc48f15fc.bcfv",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/Snapshot_4ab7514b-b216-4d56-98d2-45cf8500ff5a.png",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/Snapshot_9a4a1878-ecbd-4916-83a8-dad82e560231.png",
"7ddc3ef0-0ab7-43f1-918a-45e38b42369c/Snapshot_fc4019d7-365e-47f3-b6d0-b39fc48f15fc.png",
"d1068c81-af04-4546-b63c-348810f6c716/markup.bcf",
"extensions.xsd",
"IfcPile_01.ifc",
"markup.xsd",
]
assert_files_present(target_path, expected_files)
os.unlink(target_path)
def assert_everything_in_place(bcf: BcfXml):
assert bcf.version.version_id == "2.1"
assert bcf.project.name == "BCF API Implementation"
assert bcf.project_info.extension_schema == "extensions.xsd"
assert len(bcf.topics) == 2
assert_first_topic_handler(bcf.topics["7ddc3ef0-0ab7-43f1-918a-45e38b42369c"])
second_th = bcf.topics["d1068c81-af04-4546-b63c-348810f6c716"]
assert second_th.topic == mdl.Topic(
title="Referenced topic",
creation_date=XmlDateTime(2017, 5, 22, 7, 51, 0, 42987900),
creation_author="dangl@iabi.eu",
description="This is just an empty topic that acts as a referenced topic.",
guid="d1068c81-af04-4546-b63c-348810f6c716",
)
def assert_first_topic_handler(topic_handler: TopicHandler):
assert topic_handler.guid == "7ddc3ef0-0ab7-43f1-918a-45e38b42369c"
expected_bs1 = mdl.BimSnippet(
reference="JsonElement.json", reference_schema="http://json-schema.org", snippet_type="JSON"
)
expected_t1 = mdl.Topic(
reference_link=["https://bim--it.net"],
title="Maximum Content",
priority="High",
index=0,
labels=["Structural", "IT Development"],
creation_date=XmlDateTime(2015, 6, 21, 12, 0, 0),
creation_author="dangl@iabi.eu",
modified_date=XmlDateTime(2015, 6, 21, 14, 22, 47),
modified_author="dangl@iabi.eu",
due_date=XmlDateTime(2016, 10, 2, 14, 22, 47),
assigned_to="linhard@iabi.eu",
stage="Construction Start",
description="This is a topic with all informations present.",
bim_snippet=expected_bs1,
document_reference=[
mdl.TopicDocumentReference(
referenced_document="https://github.com/BuildingSMART/BCF-XML",
description="GitHub BCF Specification",
is_external=True,
),
mdl.TopicDocumentReference(
referenced_document="../markup.xsd",
description="Markup.xsd Schema",
is_external=False,
),
],
related_topic=[mdl.TopicRelatedTopic(guid="d1068c81-af04-4546-b63c-348810f6c716")],
guid="7ddc3ef0-0ab7-43f1-918a-45e38b42369c",
topic_type="Structural",
topic_status="Open",
)
assert topic_handler.topic == expected_t1
expected_h1 = mdl.Header(
file=[
mdl.HeaderFile(
filename="IfcPile_01.ifc",
date=XmlDateTime(2014, 10, 27, 16, 27, 27),
reference="../IfcPile_01.ifc",
ifc_project="0M6o7Znnv7hxsbWgeu7oQq",
ifc_spatial_structure_element="23B$bNeGHFQuMYJzvUX0FD",
is_external=False,
)
]
)
assert topic_handler.header == expected_h1
expected_th1_comments = [
mdl.Comment(
date=XmlDateTime(2015, 8, 31, 12, 40, 17),
author="dangl@iabi.eu",
comment="This is an unmodified topic at the uppermost hierarchical level.\nAll times in the XML are marked as UTC times.",
guid="07ccdba0-1736-47e1-807d-67dc6f3addaa",
),
mdl.Comment(
date=XmlDateTime(2015, 8, 31, 14, 0, 1),
author="dangl@iabi.eu",
comment="This comment was a reply to the first comment in BCF v2.0. This is a no longer supported functionality and therefore is to be treated as a regular comment in v2.1.",
guid="a12766c2-61bc-40b4-ab19-e9f45fd0b0bf",
),
mdl.Comment(
date=XmlDateTime(2015, 8, 31, 13, 7, 11),
author="dangl@iabi.eu",
comment="This comment again is in the highest hierarchy level.\nIt references a viewpoint.",
viewpoint=mdl.CommentViewpoint(guid="4ab7514b-b216-4d56-98d2-45cf8500ff5a"),
guid="c2bb5bb0-773d-45dd-bdaa-19a216439ed3",
),
mdl.Comment(
date=XmlDateTime(2015, 8, 31, 15, 42, 58),
author="dangl@iabi.eu",
comment="This comment contained some spllng errs.\nHopefully, the modifier did catch them all.",
modified_date=XmlDateTime(2015, 8, 31, 16, 7, 11),
modified_author="dangl@iabi.eu",
guid="0b843a5c-c3bf-41ef-be98-52a9f7bd9790",
),
]
assert topic_handler.comments == expected_th1_comments
expected_th1_viewpoints = [
mdl.ViewPoint(
viewpoint="Viewpoint_4ab7514b-b216-4d56-98d2-45cf8500ff5a.bcfv",
snapshot="Snapshot_4ab7514b-b216-4d56-98d2-45cf8500ff5a.png",
index=2,
guid="4ab7514b-b216-4d56-98d2-45cf8500ff5a",
),
mdl.ViewPoint(
viewpoint="Viewpoint_fc4019d7-365e-47f3-b6d0-b39fc48f15fc.bcfv",
snapshot="Snapshot_fc4019d7-365e-47f3-b6d0-b39fc48f15fc.png",
index=0,
guid="fc4019d7-365e-47f3-b6d0-b39fc48f15fc",
),
mdl.ViewPoint(
viewpoint="Viewpoint_9a4a1878-ecbd-4916-83a8-dad82e560231.bcfv",
snapshot="Snapshot_9a4a1878-ecbd-4916-83a8-dad82e560231.png",
index=1,
guid="9a4a1878-ecbd-4916-83a8-dad82e560231",
),
]
expected_m1 = mdl.Markup(
header=expected_h1,
topic=expected_t1,
comment=expected_th1_comments,
viewpoints=expected_th1_viewpoints,
)
assert topic_handler.markup == expected_m1
assert json.loads(topic_handler.bim_snippet) == {"Material": "Concrete", "Temperatures": ["Cold", "Hot", "Hotter"]}
assert topic_handler.reference_files["../IfcPile_01.ifc"] is not None
assert_viewpoints(topic_handler.viewpoints)
def assert_viewpoints(viewpoints):
assert len(viewpoints) == 3
expected_selection = mdl.ComponentSelection(
component=[
mdl.Component(ifc_guid="0cSRUx$EX1NRjqiKcYQ$a0"),
mdl.Component(ifc_guid="1jQQiGIAnFzxOUzrdmJYDS"),
mdl.Component(ifc_guid="0fdpeZZEX3FwJ7x0ox5kzF"),
mdl.Component(ifc_guid="23Zwlpd71EyvHlH6OZ77nK"),
mdl.Component(ifc_guid="1OpjQ1Nlv4sQuTxfUC_8zS"),
]
)
expected_exception = mdl.ComponentVisibilityExceptions(
component=[
mdl.Component(ifc_guid="0Gl71cVurFn8bxAOox6M4X"),
mdl.Component(ifc_guid="23Zwlpd71EyvHlH6OZ77nK"),
mdl.Component(ifc_guid="3DvyPxGIn8qR0KDwbL_9r1"),
mdl.Component(ifc_guid="0fdpeZZEX3FwJ7x0ox5kzF"),
mdl.Component(ifc_guid="1OpjQ1Nlv4sQuTxfUC_8zS"),
]
)
expected_coloring = mdl.ComponentColoring(
color=[
mdl.ComponentColoringColor(
component=[
mdl.Component(ifc_guid="0fdpeZZEX3FwJ7x0ox5kzF"),
mdl.Component(ifc_guid="23Zwlpd71EyvHlH6OZ77nK"),
mdl.Component(ifc_guid="1OpjQ1Nlv4sQuTxfUC_8zS"),
mdl.Component(ifc_guid="0cSRUx$EX1NRjqiKcYQ$a0"),
],
color="3498DB",
)
]
)
assert_first_viewpoint(
viewpoints["Viewpoint_4ab7514b-b216-4d56-98d2-45cf8500ff5a.bcfv"],
expected_selection,
expected_exception,
expected_coloring,
)
assert_second_viewpoint(
viewpoints["Viewpoint_fc4019d7-365e-47f3-b6d0-b39fc48f15fc.bcfv"],
expected_selection,
expected_exception,
expected_coloring,
)
assert_third_viewpoint(
viewpoints["Viewpoint_9a4a1878-ecbd-4916-83a8-dad82e560231.bcfv"],
expected_selection,
expected_exception,
expected_coloring,
)
def assert_first_viewpoint(viewpoint, expected_selection, expected_exception, expected_coloring) -> None:
expected_vp = mdl.VisualizationInfo(
components=mdl.Components(
view_setup_hints=mdl.ViewSetupHints(
spaces_visible=True,
space_boundaries_visible=True,
openings_visible=True,
),
selection=expected_selection,
visibility=mdl.ComponentVisibility(
exceptions=expected_exception,
default_visibility=True,
),
coloring=expected_coloring,
),
perspective_camera=mdl.PerspectiveCamera(
camera_view_point=mdl.Point(x=0.43079984188079834, y=69.52057647705078, z=10.666350364685059),
camera_direction=mdl.Direction(x=0.09159398823976517, y=-0.9375035166740417, z=-0.3357048034667969),
camera_up_vector=mdl.Direction(x=0.01938679628074169, y=-0.3353792130947113, z=0.9418837428092957),
field_of_view=60,
),
lines=mdl.VisualizationInfoLines(
line=[
mdl.Line(start_point=mdl.Point(x=0, y=0, z=0), end_point=mdl.Point(x=0, y=0, z=1)),
mdl.Line(start_point=mdl.Point(x=0, y=0, z=1), end_point=mdl.Point(x=0, y=1, z=1)),
mdl.Line(start_point=mdl.Point(x=0, y=1, z=1), end_point=mdl.Point(x=1, y=1, z=1)),
]
),
clipping_planes=mdl.VisualizationInfoClippingPlanes(
clipping_plane=[
mdl.ClippingPlane(location=mdl.Point(x=0, y=0, z=0), direction=mdl.Direction(x=0, y=0, z=1)),
mdl.ClippingPlane(location=mdl.Point(x=0, y=0, z=0), direction=mdl.Direction(x=0, y=1, z=0)),
]
),
bitmap=[
mdl.VisualizationInfoBitmap(
bitmap=mdl.BitmapFormat.PNG,
reference="bitmap.png",
location=mdl.Point(x=10, y=-10, z=7),
normal=mdl.Direction(x=0, y=1, z=0),
up=mdl.Direction(x=0, y=0, z=1),
height=5.0,
),
mdl.VisualizationInfoBitmap(
bitmap=mdl.BitmapFormat.PNG,
reference="tux.png",
location=mdl.Point(x=20, y=-10, z=7),
normal=mdl.Direction(x=0, y=1, z=0),
up=mdl.Direction(x=0, y=0, z=1),
height=5.0,
),
],
guid="8dc86298-9737-40b4-a448-98a9e953293a",
)
assert viewpoint.visualization_info == expected_vp
assert viewpoint.snapshot is not None
def assert_second_viewpoint(viewpoint, expected_selection, expected_exception, expected_coloring) -> None:
expected_vp = mdl.VisualizationInfo(
components=mdl.Components(
view_setup_hints=mdl.ViewSetupHints(
spaces_visible=False,
space_boundaries_visible=False,
openings_visible=False,
),
selection=expected_selection,
visibility=mdl.ComponentVisibility(
exceptions=expected_exception,
default_visibility=False,
),
coloring=expected_coloring,
),
perspective_camera=mdl.PerspectiveCamera(
camera_view_point=mdl.Point(x=-47.18794250488281, y=43.829200744628906, z=10.666350364685059),
camera_direction=mdl.Direction(x=0.6745243072509766, y=-0.6599355936050415, z=-0.33091068267822266),
camera_up_vector=mdl.Direction(x=0.2271970510482788, y=-0.24091780185699463, z=0.9435783624649048),
field_of_view=60,
),
guid="21dd4807-e9af-439e-a980-04d913a6b1ce",
)
assert viewpoint.visualization_info == expected_vp
assert viewpoint.snapshot is not None
def assert_third_viewpoint(viewpoint, expected_selection, expected_exception, expected_coloring) -> None:
expected_vp = mdl.VisualizationInfo(
components=mdl.Components(
view_setup_hints=mdl.ViewSetupHints(
spaces_visible=False,
space_boundaries_visible=False,
openings_visible=True,
),
selection=expected_selection,
visibility=mdl.ComponentVisibility(
exceptions=expected_exception,
default_visibility=True,
),
coloring=expected_coloring,
),
perspective_camera=mdl.PerspectiveCamera(
camera_view_point=mdl.Point(x=-48.974571228027344, y=-64.20051574707031, z=10.666350364685059),
camera_direction=mdl.Direction(x=0.7232745289802551, y=0.5967116951942444, z=-0.3475759029388428),
camera_up_vector=mdl.Direction(x=0.27662187814712524, y=0.21082592010498047, z=0.937567412853241),
field_of_view=60,
),
guid="81daa431-bf01-4a49-80a2-1ab07c177717",
)
assert viewpoint.visualization_info == expected_vp
assert viewpoint.snapshot is not None
def assert_files_present(saved_bcf_path, expected_files):
with zipfile.ZipFile(saved_bcf_path) as bcf_zip:
for file_path in expected_files:
assert zipfile.Path(bcf_zip, file_path).exists()
Binary file not shown.
View File
+129
View File
@@ -0,0 +1,129 @@
"""BCF XML tests."""
import uuid
from pathlib import Path
from tempfile import TemporaryDirectory
import pytest
import bcf.v3.model as mdl
from bcf.v3.bcfxml import BcfXml
from bcf.v3.topic import TopicHandler
from bcf.v3.visinfo import (
VisualizationInfoHandler,
build_camera_from_vectors,
build_components,
)
from bcf.xml_parser import XmlParserSerializer
@pytest.fixture()
def build_sample(xml_handler: XmlParserSerializer) -> tuple[BcfXml, TopicHandler]:
ext = mdl.Extensions(topic_types=mdl.ExtensionsTopicTypes(topic_type=["Test type"]))
bcf = BcfXml.create_new("Test project", extensions=ext, xml_handler=xml_handler)
orig_th = bcf.add_topic("Test topic", "Test message", "Test author", "Test type")
return bcf, orig_th
def test_bcf_roundtrip(xml_handler, build_sample) -> None:
"""Saving and loading a bcf xml project returns the same objects."""
bcf, orig_th = build_sample
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path)
with BcfXml.load(file_path, xml_handler=xml_handler) as parsed:
assert parsed == bcf
parsed_th = parsed.topics[orig_th.guid]
assert parsed_th == orig_th
def test_bcf_edit_saveas(xml_handler, build_sample) -> None:
"""Saving and loading a bcf xml project returns the same objects."""
bcf, orig_th = build_sample
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path)
with BcfXml.load(file_path, xml_handler=xml_handler) as parsed:
for th in parsed.topics.values():
th.topic.title = "New Topic Title"
modified_path = Path(tmp_dir) / "edited.bcf"
parsed.save(modified_path)
with BcfXml.load(modified_path, xml_handler=xml_handler) as modified_parsed:
assert modified_parsed == bcf
parsed_th = modified_parsed.topics[orig_th.guid]
assert parsed_th.markup != orig_th.markup
assert parsed_th.markup == parsed.topics[orig_th.guid].markup
assert parsed_th.viewpoints == orig_th.viewpoints
assert parsed_th.bim_snippet == orig_th.bim_snippet
def test_bcf_edit(xml_handler, build_sample) -> None:
"""Saving and loading a bcf xml project returns the same objects."""
bcf, orig_th = build_sample
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path)
with BcfXml.load(file_path, xml_handler=xml_handler) as parsed:
for th in parsed.topics.values():
th.topic.title = "New Topic Title"
parsed.save()
with BcfXml.load(file_path, xml_handler=xml_handler) as modified_parsed:
assert modified_parsed == bcf
assert len(modified_parsed.topics) == 1
parsed_th = modified_parsed.topics[orig_th.guid]
assert parsed_th.markup != orig_th.markup
assert parsed_th.markup == parsed.topics[orig_th.guid].markup
assert parsed_th.viewpoints == orig_th.viewpoints
assert parsed_th.bim_snippet == orig_th.bim_snippet
def test_save_no_filename(build_sample) -> None:
bcf, _ = build_sample
with pytest.raises(ValueError):
bcf.save()
def test_load_no_filename() -> None:
with pytest.raises(ValueError):
BcfXml.load("")
def test_save_keep_open(build_sample) -> None:
bcf, orig_th = build_sample
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path, keep_open=True)
assert bcf._zip_file is not None
bcf._zip_file.close()
# image = PIL.Image.new('RGB', size=(100, 100))
# file = BinaryIO()
# image.save(file)
def test_massive_bcf(xml_handler) -> None:
ext = mdl.Extensions(topic_types=mdl.ExtensionsTopicTypes(topic_type=["Test type"]))
bcf = BcfXml.create_new("Test project", extensions=ext, xml_handler=xml_handler)
for i in range(100):
th = bcf.add_topic(f"Topic {i:04}", f"Message {i:04}", "Test author", "Test type")
vi = mdl.VisualizationInfo(
guid=str(uuid.uuid4()),
components=build_components(str(uuid.uuid4())),
perspective_camera=build_camera_from_vectors([i, 0, 0], [0, 1, 0], [0, 0, 1]),
)
vh = VisualizationInfoHandler(visualization_info=vi, xml_handler=xml_handler)
th.add_visinfo_handler(vh)
assert len(bcf.topics) == 100
with TemporaryDirectory() as tmp_dir:
file_path = Path(tmp_dir) / "test.bcf"
bcf.save(file_path)
def test_equality_with_wrong_object() -> None:
with pytest.raises(TypeError):
build_sample[0] == "Wrong object"
def test_topic_equality_with_wrong_object() -> None:
with pytest.raises(TypeError):
build_sample[1] == "Wrong object"
+230
View File
@@ -0,0 +1,230 @@
import json
import os
import zipfile
from pathlib import Path
from xsdata.models.datatype import XmlDateTime
import bcf.v3.model as mdl
from bcf.v3.bcfxml import BcfXml
from bcf.v3.topic import TopicHandler
def test_doc_ref_internal() -> None:
"""
All the info in a BCF is parsed correctly.
Uses sample file from https://github.com/buildingSMART/BCF-XML/
"""
bcf_path = Path(__file__).parent / "Document reference internal.bcf"
with BcfXml.load(bcf_path) as bcf:
assert_everything_in_place(bcf)
def test_doc_ref_internal_save() -> None:
base_dir = Path(__file__).parent
bcf_path = base_dir / "Document reference internal.bcf"
target_path = base_dir / "Document reference internal Saved.bcf"
with BcfXml.load(bcf_path) as bcf:
bcf.save(target_path)
assert target_path.exists()
with BcfXml.load(target_path) as bcf2:
assert_everything_in_place(bcf2)
assert_files_present(target_path)
os.unlink(target_path)
def assert_everything_in_place(bcf: BcfXml):
assert bcf.version.version_id == "3.0"
assert bcf.project.name == "BCF 3.0 test cases"
assert bcf.project.project_id == "de894a86-3a08-4ea0-b2d1-6c222b5602d1"
assert len(bcf.topics) == 1
topic_handler = bcf.topics["8ac9822a-761a-4deb-9f39-f61286acbf6a"]
expected_h1 = mdl.Header(
files=mdl.HeaderFiles(
file=[
mdl.File(
filename="BCF-ARK",
date=XmlDateTime(2021, 1, 4, 9, 37, 45),
reference="https://bimsync.com/project/f5fbb3c695274d1890036bf64f77eb71/revisions/007afab57f264d2296aae0a452486ae1",
is_external=True,
),
mdl.File(
filename="BCF-MEP",
date=XmlDateTime(2017, 8, 7, 9, 51, 34),
reference="https://bimsync.com/project/f5fbb3c695274d1890036bf64f77eb71/revisions/a21ed391f9e046a2bb2bc879c48f1d48",
is_external=True,
),
]
)
)
assert topic_handler.header == expected_h1
expected_comments = [
mdl.Comment(
date=XmlDateTime(2021, 2, 17, 9, 16, 4, 160_000_000),
author="Architect@example.com",
comment="A comment",
viewpoint=mdl.CommentViewpoint(guid="20ad2ff7-ceac-4d4b-b288-ad92a0f65182"),
guid="c045ef04-324d-4c36-9ac8-831f2a15c6d6",
),
]
assert topic_handler.comments == expected_comments
expected_viewpoints = [
mdl.ViewPoint(
viewpoint="Viewpoint_20ad2ff7-ceac-4d4b-b288-ad92a0f65182.bcfv",
snapshot="Snapshot_20ad2ff7-ceac-4d4b-b288-ad92a0f65182.png",
guid="20ad2ff7-ceac-4d4b-b288-ad92a0f65182",
),
]
expected_topic = mdl.Topic(
reference_links=mdl.TopicReferenceLinks(),
title="Document Reference Internal",
labels=mdl.TopicLabels(),
creation_date=XmlDateTime(2021, 2, 17, 9, 16, 4, 160_000_000),
creation_author="Architect@example.com",
modified_date=XmlDateTime(2021, 2, 17, 9, 16, 4, 160_000_000),
assigned_to="OtherUser@doe.com",
document_references=mdl.TopicDocumentReferences(
document_reference=[
mdl.DocumentReference(
document_guid="b1d1b7f0-60b9-457d-ad12-16e0fb997bc5",
description="ThisIsADocument.txt",
guid="048e898f-555f-47c3-a273-6c664fb2ef69",
),
],
),
related_topics=mdl.TopicRelatedTopics(),
comments=mdl.TopicComments(comment=expected_comments),
viewpoints=mdl.TopicViewpoints(view_point=expected_viewpoints),
guid="8ac9822a-761a-4deb-9f39-f61286acbf6a",
server_assigned_id="6",
topic_type="ERROR",
topic_status="OPEN",
)
assert topic_handler.topic == expected_topic
expected_m1 = mdl.Markup(
header=expected_h1,
topic=expected_topic,
)
assert topic_handler.markup == expected_m1
assert topic_handler.bim_snippet is None
assert_viewpoints(topic_handler.viewpoints)
def assert_viewpoints(viewpoints):
assert len(viewpoints) == 1
expected_vp = mdl.VisualizationInfo(
components=mdl.Components(
selection=mdl.ComponentSelection(),
visibility=mdl.ComponentVisibility(
view_setup_hints=mdl.ViewSetupHints(
spaces_visible=False,
space_boundaries_visible=False,
openings_visible=False,
),
exceptions=mdl.ComponentVisibilityExceptions(
component=[
mdl.Component(ifc_guid="1m5wAJelDFdhn6qBdOGjos", originating_system="https://bimsync.com"),
mdl.Component(ifc_guid="1bbI761TbBCOoIa5Kt6PXt", originating_system="https://bimsync.com"),
]
),
default_visibility=True,
),
coloring=mdl.ComponentColoring(),
),
perspective_camera=mdl.PerspectiveCamera(
camera_view_point=mdl.Point(x=23.112083480037754, y=-25.45574897560043, z=18.52519828443092),
camera_direction=mdl.Direction(x=-0.6289237666876837, y=0.5933487270610425, z=-0.5023864884632315),
camera_up_vector=mdl.Direction(x=-0.36542566067664123, y=0.3447553774917179, z=0.8646431727652648),
field_of_view=60,
aspect_ratio=1,
),
lines=mdl.VisualizationInfoLines(),
clipping_planes=mdl.VisualizationInfoClippingPlanes(),
bitmaps=mdl.VisualizationInfoBitmaps(),
guid="20ad2ff7-ceac-4d4b-b288-ad92a0f65182",
)
viewpoint = viewpoints["Viewpoint_20ad2ff7-ceac-4d4b-b288-ad92a0f65182.bcfv"]
assert viewpoint.visualization_info == expected_vp
assert viewpoint.snapshot is not None
def assert_second_viewpoint(viewpoint, expected_selection, expected_exception, expected_coloring) -> None:
expected_vp = mdl.VisualizationInfo(
components=mdl.Components(
view_setup_hints=mdl.ViewSetupHints(
spaces_visible=False,
space_boundaries_visible=False,
openings_visible=False,
),
selection=expected_selection,
visibility=mdl.ComponentVisibility(
exceptions=expected_exception,
default_visibility=False,
),
coloring=expected_coloring,
),
perspective_camera=mdl.PerspectiveCamera(
camera_view_point=mdl.Point(x=-47.18794250488281, y=43.829200744628906, z=10.666350364685059),
camera_direction=mdl.Direction(x=0.6745243072509766, y=-0.6599355936050415, z=-0.33091068267822266),
camera_up_vector=mdl.Direction(x=0.2271970510482788, y=-0.24091780185699463, z=0.9435783624649048),
field_of_view=60,
),
guid="21dd4807-e9af-439e-a980-04d913a6b1ce",
)
assert viewpoint.visualization_info == expected_vp
assert viewpoint.snapshot is not None
def assert_third_viewpoint(viewpoint, expected_selection, expected_exception, expected_coloring) -> None:
expected_vp = mdl.VisualizationInfo(
components=mdl.Components(
view_setup_hints=mdl.ViewSetupHints(
spaces_visible=False,
space_boundaries_visible=False,
openings_visible=True,
),
selection=expected_selection,
visibility=mdl.ComponentVisibility(
exceptions=expected_exception,
default_visibility=True,
),
coloring=expected_coloring,
),
perspective_camera=mdl.PerspectiveCamera(
camera_view_point=mdl.Point(x=-48.974571228027344, y=-64.20051574707031, z=10.666350364685059),
camera_direction=mdl.Direction(x=0.7232745289802551, y=0.5967116951942444, z=-0.3475759029388428),
camera_up_vector=mdl.Direction(x=0.27662187814712524, y=0.21082592010498047, z=0.937567412853241),
field_of_view=60,
),
guid="81daa431-bf01-4a49-80a2-1ab07c177717",
)
assert viewpoint.visualization_info == expected_vp
assert viewpoint.snapshot is not None
def assert_files_present(saved_bcf_path):
expected_files = [
"bcf.version",
"project.bcfp",
"documents.xml",
"extensions.xml",
"documents/b1d1b7f0-60b9-457d-ad12-16e0fb997bc5",
"8ac9822a-761a-4deb-9f39-f61286acbf6a/markup.bcf",
"8ac9822a-761a-4deb-9f39-f61286acbf6a/Viewpoint_20ad2ff7-ceac-4d4b-b288-ad92a0f65182.bcfv",
"8ac9822a-761a-4deb-9f39-f61286acbf6a/Snapshot_20ad2ff7-ceac-4d4b-b288-ad92a0f65182.png",
]
with zipfile.ZipFile(saved_bcf_path) as bcf_zip:
for file_path in expected_files:
assert zipfile.Path(bcf_zip, file_path).exists()