"""Scene package provenance and connector-binding fixture builders.""" from __future__ import annotations from copy import deepcopy from typing import Callable from simplecadapi.scene import with_scene_revision from .common import ( JsonObject, ScenePackage, inline_package_case, replace_entity_sidecar, set_product_material_appearance, ) def build_source_scene_package( base_scene: JsonObject, base_entity: JsonObject, base_blobs: dict[str, bytes], variant: str, ) -> ScenePackage: scene = deepcopy(base_scene) entity = deepcopy(base_entity) definition = scene["definitions"][0] source_for: Callable[[JsonObject], JsonObject] status_for: Callable[[JsonObject], str] if variant == "imported_shape": definition_id = "definition/root/shape/imported/element" scene["scene_id"] = "imported_fixture" scene["source"] = { "artifact_hash": "sha256:" + "1" * 64, "format": "step", "kind": "imported", } definition.update( { "definition_id": definition_id, "kind": "shape", "source": { "kind": "imported", "root_id": "root", "source_element_id": "element", }, } ) source_for = lambda record: { "kind": "imported_primitive", "source_element_id": record["entity_id"], } status_for = lambda record: ( "not_applicable" if record["kind"] == "solid" else "owner_not_part" ) else: definition_kind = "part" if variant == "model_part" else "shape" definition_id = ( "definition/root/part/model_part" if definition_kind == "part" else "definition/root/shape/model/graph/body/0" ) scene["scene_id"] = f"{variant}_fixture" scene["source"] = { "artifact_hash": "sha256:" + "2" * 64, "graph_id": "graph", "kind": "model", "model_schema_version": "2.0", } definition_source: JsonObject = { "graph_id": "graph", "kind": "product_model" if definition_kind == "part" else "model_output", "node_id": "body", "output_slot": 0, "root_id": "root", } if definition_kind == "part": definition_source.update( {"semantic_id": "model_part", "semantic_type": "Part"} ) definition.update( { "definition_id": definition_id, "kind": definition_kind, "source": definition_source, } ) topology_kind = { "solid": "SOLID", "face": "FACE", "edge": "EDGE", "vertex": "VERTEX", } if variant == "model_part": source_for = lambda record: { "graph_id": "graph", "kind": "model_topology", "node_id": "body", "output_slot": 0, "topo_id": record["entity_id"], "topology_kind": topology_kind[record["kind"]], } statuses = iter( ["supported", "selector_ambiguous", "selector_unstable", "supported"] ) def status_for(record: JsonObject) -> str: return ( "not_applicable" if record["kind"] == "solid" else next(statuses, "supported") ) else: source_for = lambda _record: { "graph_id": "graph", "kind": "model_output", "node_id": "body", "output_slot": 0, } status_for = lambda record: ( "not_applicable" if record["kind"] == "solid" else "owner_not_part" ) if definition["kind"] == "part": scene["nodes"][0]["source"] = { "component_path": [], "kind": "product_occurrence", "root_id": "root", } scene["nodes"][0]["definition_id"] = definition_id entity["definition_id"] = definition_id for record in entity["entities"]: record["source"] = source_for(record) record["connector_binding_status"] = status_for(record) if variant == "model_part": edge = next(record for record in entity["entities"] if record["kind"] == "edge") edge["sdk_connector_frame"] = None edge["connector_binding_status"] = "frame_undefined" scene, entity, blobs = replace_entity_sidecar( scene, entity, dict(base_blobs) ) if definition["kind"] == "part": set_product_material_appearance(scene) return with_scene_revision(scene), entity, blobs def build_package_matrix_cases( scene: JsonObject, entity: JsonObject, blobs: dict[str, bytes], scene_packages: dict[str, ScenePackage], blob_pool: dict[str, str], ) -> list[dict[str, object]]: cases: list[dict[str, object]] = [] for variant in ("imported_shape", "model_shape", "model_part"): package = build_source_scene_package(scene, entity, blobs, variant) cases.append( inline_package_case( f"valid_{variant}", package[0], package[2], blob_pool ) ) nested = scene_packages["nested_assembly"] model_part = build_source_scene_package(scene, entity, blobs, "model_part") status_mutations = [ ( "solid_status_precedence", (deepcopy(scene), deepcopy(entity), dict(blobs)), "solid", "supported", ), ( "owner_not_part_status_precedence", (deepcopy(scene), deepcopy(entity), dict(blobs)), "face", "source_not_model", ), ( "source_not_model_status_precedence", deepcopy(nested), "face", "supported", ), ( "frame_undefined_status_precedence", deepcopy(model_part), "edge", "owner_not_part", ), ] for name, package, kind, status in status_mutations: status_scene, status_entity, status_blobs = package target = next( record for record in status_entity["entities"] if record["kind"] == kind ) target["connector_binding_status"] = status status_scene, _status_entity, status_blobs = replace_entity_sidecar( status_scene, status_entity, status_blobs ) cases.append( inline_package_case( name, with_scene_revision(status_scene), status_blobs, blob_pool ) ) forwarded_scene, _forwarded_entity, forwarded_blobs = deepcopy(nested) root_connector = next( record for record in forwarded_scene["connectors"] if record["connector_id"] == "root_mount" ) root_connector["local_transform"]["origin"] = [0, 0, 6] cases.append( inline_package_case( "forwarded_transform_mismatch", with_scene_revision(forwarded_scene), forwarded_blobs, blob_pool, ) ) missing_child_scene, _missing_child_entity, missing_child_blobs = deepcopy(nested) root_connector = next( record for record in missing_child_scene["connectors"] if record["connector_id"] == "root_mount" ) root_connector["forwarded_from"]["source_component_id"] = "missing" cases.append( inline_package_case( "forwarded_direct_child_missing", with_scene_revision(missing_child_scene), missing_child_blobs, blob_pool, ) ) part_owner_scene, _part_owner_entity, part_owner_blobs = deepcopy(nested) root_connector = next( record for record in part_owner_scene["connectors"] if record["connector_id"] == "root_mount" ) root_connector["owner_definition_id"] = "definition/root/part/shared_part" root_connector["connector_snapshot_id"] = ( "connector/root/part/shared_part/root_mount" ) part_owner_scene["connectors"].sort( key=lambda record: record["connector_snapshot_id"] ) cases.append( inline_package_case( "forwarded_owner_not_assembly", with_scene_revision(part_owner_scene), part_owner_blobs, blob_pool, ) ) return cases