s3dgraphy 1.6.0.dev6__tar.gz → 1.6.0.dev7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. {s3dgraphy-1.6.0.dev6/src/s3dgraphy.egg-info → s3dgraphy-1.6.0.dev7}/PKG-INFO +1 -1
  2. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/pyproject.toml +1 -1
  3. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/__init__.py +1 -1
  4. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/canvas_generator.py +22 -1
  5. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/graphml_exporter.py +38 -1
  6. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/node_generator.py +33 -3
  7. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/import_graphml.py +184 -9
  8. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/graph_ingestor.py +39 -245
  9. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/graph_projector.py +31 -44
  10. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/graphml_writer.py +10 -57
  11. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/rapporti.py +551 -0
  12. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/templates/em_palette_template.graphml +11 -0
  13. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7/src/s3dgraphy.egg-info}/PKG-INFO +1 -1
  14. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy.egg-info/SOURCES.txt +1 -0
  15. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/LICENSE +0 -0
  16. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/MANIFEST.in +0 -0
  17. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/README.md +0 -0
  18. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/setup.cfg +0 -0
  19. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em.ttl +0 -0
  20. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_document_types.json +0 -0
  21. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_extractor_types.json +0 -0
  22. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_palette_icons.json +0 -0
  23. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_qualia_types.json +0 -0
  24. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_qualia_types_additions.json +0 -0
  25. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_visual_rules.json +0 -0
  26. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/hdto_extension.ttl +0 -0
  27. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/new_qualia_template.json +0 -0
  28. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/outdated_s3dgraphy.ttl +0 -0
  29. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/outdated_s3dgraphy_nomapping.ttl +0 -0
  30. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/s3Dgraphy_connections_datamodel.json +0 -0
  31. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/s3Dgraphy_connections_datamodel.json.v155.bak +0 -0
  32. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/s3Dgraphy_node_datamodel.json +0 -0
  33. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/RSF.png +0 -0
  34. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/RSF.svg +0 -0
  35. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/SF.png +0 -0
  36. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/TSU.png +0 -0
  37. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/US.png +0 -0
  38. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/USD.png +0 -0
  39. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/USVn.png +0 -0
  40. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/USVs.png +0 -0
  41. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/VSF.png +0 -0
  42. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/combiner.png +0 -0
  43. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/continuity.png +0 -0
  44. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/document.png +0 -0
  45. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/extractor.png +0 -0
  46. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/property.png +0 -0
  47. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/serSU.png +0 -0
  48. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/serUSD.png +0 -0
  49. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/serUSV.png +0 -0
  50. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/RSF.glb +0 -0
  51. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/SF.glb +0 -0
  52. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/TSU.glb +0 -0
  53. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/US.glb +0 -0
  54. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/USD.glb +0 -0
  55. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/USVn.glb +0 -0
  56. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/USVs.glb +0 -0
  57. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/VSF.glb +0 -0
  58. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/combiner.glb +0 -0
  59. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/continuity.glb +0 -0
  60. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/document.glb +0 -0
  61. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/extractor.glb +0 -0
  62. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/property.glb +0 -0
  63. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/serSU.glb +0 -0
  64. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/serUSD.glb +0 -0
  65. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/serUSV.glb +0 -0
  66. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/classification.py +0 -0
  67. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/data/StratiMiner_Extraction_Prompt.md +0 -0
  68. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/diagnostics.py +0 -0
  69. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/edges/__init__.py +0 -0
  70. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/edges/connections_loader.py +0 -0
  71. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/edges/edge.py +0 -0
  72. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/__init__.py +0 -0
  73. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/__init__.py +0 -0
  74. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/edge_generator.py +0 -0
  75. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/epoch_generator.py +0 -0
  76. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/graphml_patcher.py +0 -0
  77. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/group_node_generator.py +0 -0
  78. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/node_registry.py +0 -0
  79. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/palette_resources.py +0 -0
  80. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/paradata_generator.py +0 -0
  81. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/paradata_image_generator.py +0 -0
  82. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/paradata_node_generators.py +0 -0
  83. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/table_node_generator.py +0 -0
  84. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/utils.py +0 -0
  85. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/json_exporter.py +0 -0
  86. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/rdf_exporter.py +0 -0
  87. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/unified_xlsx_exporter.py +0 -0
  88. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/graph.py +0 -0
  89. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/__init__.py +0 -0
  90. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/base_importer.py +0 -0
  91. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/mapped_xlsx_importer.py +0 -0
  92. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/pyarchinit_importer.py +0 -0
  93. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/qualia_importer.py +0 -0
  94. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/unified_xlsx_importer.py +0 -0
  95. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/xlsx_importer.py +0 -0
  96. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/indices.py +0 -0
  97. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/__init__.py +0 -0
  98. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/emdb/generic_specialfind_mapping.json +0 -0
  99. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/emdb/site_properties_mapping.json +0 -0
  100. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/emdb/usm_mapping.json +0 -0
  101. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/generic/excel_to_graphml_mapping.json +0 -0
  102. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/pyarchinit/pyarchinit_us_mapping.json +0 -0
  103. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/registry.py +0 -0
  104. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/template_emdb_mapping.json +0 -0
  105. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/template_pyarchinit_mapping.json +0 -0
  106. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/merge/__init__.py +0 -0
  107. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/merge/graph_merger.py +0 -0
  108. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/multigraph/__init__.py +0 -0
  109. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/multigraph/multigraph.py +0 -0
  110. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/__init__.py +0 -0
  111. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/author_node.py +0 -0
  112. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/base_node.py +0 -0
  113. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/combiner_node.py +0 -0
  114. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/document_node.py +0 -0
  115. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/embargo_node.py +0 -0
  116. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/epoch_node.py +0 -0
  117. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/extractor_node.py +0 -0
  118. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/geo_position_node.py +0 -0
  119. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/graph_node.py +0 -0
  120. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/group_node.py +0 -0
  121. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/hdt_node.py +0 -0
  122. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/license_node.py +0 -0
  123. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/link_node.py +0 -0
  124. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/paradata_node.py +0 -0
  125. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/property_node.py +0 -0
  126. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/representation_node.py +0 -0
  127. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/semantic_shape_node.py +0 -0
  128. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/stratigraphic_node.py +0 -0
  129. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/resolvers/__init__.py +0 -0
  130. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/resolvers/builtin_rules.py +0 -0
  131. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/resolvers/property_resolver.py +0 -0
  132. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/__init__.py +0 -0
  133. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/_db_handle.py +0 -0
  134. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/_legacy_paradata_svgs.py +0 -0
  135. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/_workspace.py +0 -0
  136. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/conflict_resolver.py +0 -0
  137. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/edge_registry.py +0 -0
  138. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/group_projector.py +0 -0
  139. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/group_store.py +0 -0
  140. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/ingest_result.py +0 -0
  141. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/paradata_store.py +0 -0
  142. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/pyarchinit_pg_importer.py +0 -0
  143. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/uuid7.py +0 -0
  144. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/vocab_provider_core.py +0 -0
  145. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/vocab_types.py +0 -0
  146. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/yed_classifier.py +0 -0
  147. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/yed_detector.py +0 -0
  148. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/yed_group_walker.py +0 -0
  149. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/yed_import_pipeline.py +0 -0
  150. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/yed_rapporti_policy.py +0 -0
  151. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/sync/yed_table_parser.py +0 -0
  152. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/templates/em_data_template.xlsx +0 -0
  153. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/temporal/__init__.py +0 -0
  154. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/temporal/inference_engine.py +0 -0
  155. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/transforms/__init__.py +0 -0
  156. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/transforms/aux_tracking.py +0 -0
  157. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/transforms/compact.py +0 -0
  158. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/transforms/materialize_continuity.py +0 -0
  159. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/utils/__init__.py +0 -0
  160. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/utils/utils.py +0 -0
  161. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/utils/visual_layout.py +0 -0
  162. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy.egg-info/dependency_links.txt +0 -0
  163. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy.egg-info/requires.txt +0 -0
  164. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy.egg-info/top_level.txt +0 -0
  165. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/tests/test_composite_node_name.py +0 -0
  166. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/tests/test_filtered_import.py +0 -0
  167. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/tests/test_lossless_roundtrip.py +0 -0
  168. {s3dgraphy-1.6.0.dev6 → s3dgraphy-1.6.0.dev7}/tests/test_pg_importer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: s3dgraphy
3
- Version: 1.6.0.dev6
3
+ Version: 1.6.0.dev7
4
4
  Summary: 3D Stratigraphic Graph Management Library for archaeological and heritage applications
5
5
  Author-email: Emanuel Demetrescu <emanuel.demetrescu@cnr.it>
6
6
  Maintainer-email: Emanuel Demetrescu <emanuel.demetrescu@cnr.it>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "s3dgraphy"
7
- version = "1.6.0.dev6"
7
+ version = "1.6.0.dev7"
8
8
  description = "3D Stratigraphic Graph Management Library for archaeological and heritage applications"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,4 +1,4 @@
1
- __version__ = "1.6.0.dev6"
1
+ __version__ = "1.6.0.dev7"
2
2
  __datamodel_version__ = "1.5.5" # s3Dgraphy connections datamodel version
3
3
 
4
4
  # s3Dgraphy/__init__.py
@@ -68,7 +68,7 @@ class CanvasGenerator:
68
68
  return root
69
69
 
70
70
  def _add_node_keys(self, root: ET.Element):
71
- """Add key definitions for nodes (d4-d8)."""
71
+ """Add key definitions for nodes (d4-d8, d13)."""
72
72
  # d4: url
73
73
  key = ET.SubElement(root, '{http://graphml.graphdrawing.org/xmlns}key')
74
74
  key.set('attr.name', 'url')
@@ -104,6 +104,27 @@ class CanvasGenerator:
104
104
  key.set('for', 'node')
105
105
  key.set('id', 'd8')
106
106
 
107
+ # d13: physical_relationships (EM 1.6 per-node packed string)
108
+ #
109
+ # Per-node serialisation of the canonical physical stratigraphic
110
+ # edges, in the same list-of-lists Python-literal format used by
111
+ # the pyArchInit ``us_table.rapporti`` column. yEd reserves edges
112
+ # for the temporal Matrix layer, so the EM 1.6 palette surfaces
113
+ # these relationships as a per-node attribute instead — visible
114
+ # to humans editing the file in yEd, and byte-identical with the
115
+ # pyArchInit column when the graph between them is unmutated.
116
+ #
117
+ # The exporter writes this attribute on every stratigraphic node
118
+ # whose canonical edges include physical-stratigraphic types;
119
+ # the importer reads it only when the richer graph-level JSON
120
+ # side channel (``_s3d_physical_relations``) is absent. See
121
+ # ``s3dgraphy.sync.rapporti`` for the canonical vocabulary.
122
+ key = ET.SubElement(root, '{http://graphml.graphdrawing.org/xmlns}key')
123
+ key.set('attr.name', 'physical_relationships')
124
+ key.set('attr.type', 'string')
125
+ key.set('for', 'node')
126
+ key.set('id', 'd13')
127
+
107
128
  def _add_edge_keys(self, root: ET.Element):
108
129
  """Add key definitions for edges (d10-d12)."""
109
130
  # d10: edgegraphics (yfiles)
@@ -142,6 +142,40 @@ class GraphMLExporter:
142
142
  # side channel restores all three on import.
143
143
  self._write_property_metadata_data(graph_elem, canvas)
144
144
 
145
+ # 1d. Snapshot per-US-node physical_relationships (the EM 1.6
146
+ # palette per-node packed string format, byte-identical with the
147
+ # pyArchInit ``us_table.rapporti`` column). Computed BEFORE the
148
+ # transitive reduction below so the canonical edges are still
149
+ # present; stashed for NodeGenerator to attach as ``<data
150
+ # key="d13">`` on each stratigraphic node. See
151
+ # ``s3dgraphy.sync.rapporti.serialize_rapporti_from_edges`` for
152
+ # the format and ``canvas_generator._add_node_keys`` (d13) for
153
+ # the key declaration.
154
+ try:
155
+ from ...sync.rapporti import serialize_rapporti_from_edges
156
+ # Pick the site name to stamp into the 4th column of each
157
+ # rapporto entry. Graph.__init__ stores the caller-supplied
158
+ # identifier on ``graph_id`` and leaves ``name`` empty by
159
+ # default, so ``graph_id`` is the most reliable signal in
160
+ # practice — but we still consult ``name`` / ``sito`` for
161
+ # consumers that populate them explicitly.
162
+ default_sito = (
163
+ getattr(self.graph, "graph_id", None)
164
+ or getattr(self.graph, "name", None)
165
+ or getattr(self.graph, "sito", None)
166
+ or ""
167
+ )
168
+ # ``name`` may be a dict in the s3dgraphy Graph schema; only
169
+ # str values are valid for the pyArchInit column.
170
+ if not isinstance(default_sito, str):
171
+ default_sito = ""
172
+ self._physical_relationships_by_node: Dict[str, list] = (
173
+ serialize_rapporti_from_edges(self.graph, default_sito))
174
+ except Exception as exc: # pragma: no cover - defensive
175
+ print(f" [physical_relationships] skipped serialization: "
176
+ f"{exc}")
177
+ self._physical_relationships_by_node = {}
178
+
145
179
  # Allocate palette refids starting from 4 (1-3 are reserved for
146
180
  # SVG extractor/combiner/continuity emitted by CanvasGenerator).
147
181
  # Populated as AuthorNode / LicenseNode / EmbargoNode instances
@@ -150,7 +184,10 @@ class GraphMLExporter:
150
184
  self._next_palette_refid: int = 4
151
185
 
152
186
  # 2. Initialize generators
153
- node_gen = NodeGenerator(self.node_registry, self.id_manager)
187
+ node_gen = NodeGenerator(
188
+ self.node_registry, self.id_manager,
189
+ physical_relationships_by_node=self._physical_relationships_by_node,
190
+ )
154
191
  group_gen = GroupNodeGenerator(self.node_registry, self.id_manager)
155
192
  edge_gen = EdgeGenerator(self.id_manager)
156
193
 
@@ -15,17 +15,30 @@ from .utils import IDManager, calculate_node_width, generate_uuid
15
15
  class NodeGenerator:
16
16
  """Generates GraphML XML for various node types."""
17
17
 
18
- def __init__(self, registry: NodeRegistry, id_manager: IDManager):
18
+ def __init__(self, registry: NodeRegistry, id_manager: IDManager,
19
+ physical_relationships_by_node: Optional[dict] = None):
19
20
  """
20
21
  Initialize node generator.
21
-
22
+
22
23
  Args:
23
24
  registry: Node registry with visual properties
24
25
  id_manager: ID manager for nested IDs
26
+ physical_relationships_by_node: Optional pre-computed dict
27
+ mapping ``node_id`` → list of
28
+ ``[label, target_us, area, sito]`` entries (the
29
+ pyArchInit list-of-lists serialisation produced by
30
+ :func:`s3dgraphy.sync.rapporti.serialize_rapporti_from_edges`).
31
+ When provided, ``generate_stratigraphic_node`` emits a
32
+ ``<data key="d13">`` element with the Python-literal
33
+ ``repr`` of the entry list — the EM 1.6 palette format
34
+ for per-node physical relationships. Unknown / missing
35
+ node ids are silently ignored.
25
36
  """
26
37
  self.registry = registry
27
38
  self.id_manager = id_manager
28
39
  self.ns_y = 'http://www.yworks.com/xml/graphml'
40
+ self._physical_relationships_by_node = (
41
+ physical_relationships_by_node or {})
29
42
 
30
43
  def generate_stratigraphic_node(self, node, x: float = 100.0, y: float = 100.0,
31
44
  parent_id: Optional[str] = None) -> ET.Element:
@@ -72,7 +85,24 @@ class NodeGenerator:
72
85
  data_d7 = ET.SubElement(node_elem, '{http://graphml.graphdrawing.org/xmlns}data')
73
86
  data_d7.set('key', 'd7')
74
87
  data_d7.text = node_uuid
75
-
88
+
89
+ # Add physical_relationships (d13) — EM 1.6 per-node packed
90
+ # string. The exporter pre-computes a dict ``node_id → list``
91
+ # using :func:`s3dgraphy.sync.rapporti.serialize_rapporti_from_edges`
92
+ # before the transitive reduction collapses canonical edges, and
93
+ # passes it in via ``__init__``. The on-disk format is the
94
+ # Python literal ``repr`` of the list-of-lists, byte-identical
95
+ # with the pyArchInit ``us_table.rapporti`` column when the
96
+ # graph between them is unmutated.
97
+ rapporti_entries = self._physical_relationships_by_node.get(
98
+ node_uuid)
99
+ if rapporti_entries:
100
+ data_d13 = ET.SubElement(
101
+ node_elem,
102
+ '{http://graphml.graphdrawing.org/xmlns}data')
103
+ data_d13.set('key', 'd13')
104
+ data_d13.text = repr(rapporti_entries)
105
+
76
106
  # Add nodegraphics (d6) - ShapeNode
77
107
  data_d6 = ET.SubElement(node_elem, '{http://graphml.graphdrawing.org/xmlns}data')
78
108
  data_d6.set('key', 'd6')
@@ -362,7 +362,17 @@ class GraphMLImporter:
362
362
  # exporter. Legacy files without the key are left untouched (the
363
363
  # reduced is_after / has_same_time set from parse_edges remains
364
364
  # in place). See module-level docstring for the rationale.
365
- self._restore_physical_relations_from_side_channel(tree)
365
+ json_channel_applied = (
366
+ self._restore_physical_relations_from_side_channel(tree))
367
+
368
+ # Fallback for files NOT produced by the lossless exporter: read
369
+ # per-node ``physical_relationships`` (d13) packed strings — the
370
+ # EM 1.6 palette / pyArchInit-native list-of-lists format — and
371
+ # rebuild canonical edges from them. Skipped when the
372
+ # graph-level JSON side channel was present, since that's
373
+ # strictly richer (carries per-edge attributes and edge_ids).
374
+ if not json_channel_applied:
375
+ self._restore_physical_relations_from_node_attribute(tree)
366
376
 
367
377
  # Re-apply structured PropertyNode metadata (value /
368
378
  # property_type / units / attributes) from the
@@ -759,7 +769,7 @@ class GraphMLImporter:
759
769
 
760
770
  # print(f"Warning: Could not create edge {original_edge_id} - Source: {original_source_id} -> Target: {original_target_id}")
761
771
 
762
- def _restore_physical_relations_from_side_channel(self, tree):
772
+ def _restore_physical_relations_from_side_channel(self, tree) -> bool:
763
773
  """Restore the original physical stratigraphic edges from the
764
774
  ``_s3d_physical_relations`` graph-level GraphML key, if present.
765
775
 
@@ -787,6 +797,13 @@ class GraphMLImporter:
787
797
  ``parse_edges`` / ``process_node_element``), the UUIDs round-trip
788
798
  directly. Unknown UUIDs are skipped with a warning so a partial
789
799
  graph still imports cleanly.
800
+
801
+ Returns:
802
+ ``True`` if the graph-level key was *declared* on the file
803
+ (regardless of payload size), ``False`` otherwise. Callers
804
+ use this to gate the per-node ``physical_relationships``
805
+ (d13) fallback: an authoritative JSON channel should not be
806
+ shadowed by the strictly poorer per-node packed string.
790
807
  """
791
808
  root = tree.getroot()
792
809
  ns = 'http://graphml.graphdrawing.org/xmlns'
@@ -803,8 +820,10 @@ class GraphMLImporter:
803
820
  break
804
821
 
805
822
  if not side_channel_key_id:
806
- # Legacy file: no key declared, nothing to restore.
807
- return
823
+ # Legacy file: no key declared, nothing to restore. The
824
+ # per-node ``physical_relationships`` (d13) fallback may
825
+ # still apply — callers gate on the return value.
826
+ return False
808
827
 
809
828
  # 2) Find the graph-level <data> element carrying the payload.
810
829
  # We expect it directly under <graph id="G">. Scan all top-level
@@ -818,28 +837,34 @@ class GraphMLImporter:
818
837
  side_channel_data = candidate
819
838
  break
820
839
 
840
+ # From here on, the key was declared — the file was authored by
841
+ # the lossless exporter (or a tool that knows about it). Return
842
+ # True at every exit point so the per-node fallback stays out of
843
+ # the way; the JSON channel is authoritative.
821
844
  if side_channel_data is None or not (side_channel_data.text or "").strip():
822
- # Key declared but no payload — treat as legacy.
823
- return
845
+ # Key declared but no payload — treat as "no relations" but
846
+ # don't fall back to d13 (that would re-introduce stale data
847
+ # if the producer intentionally cleared the channel).
848
+ return True
824
849
 
825
850
  try:
826
851
  relations = json.loads(side_channel_data.text)
827
852
  except (ValueError, TypeError) as exc:
828
853
  print(f"[GraphML Parser] WARNING: could not parse "
829
854
  f"{_S3D_PHYSICAL_RELATIONS_ATTR_NAME} payload: {exc}")
830
- return
855
+ return True
831
856
 
832
857
  if not isinstance(relations, list):
833
858
  print(f"[GraphML Parser] WARNING: "
834
859
  f"{_S3D_PHYSICAL_RELATIONS_ATTR_NAME} payload is not a "
835
860
  f"list (got {type(relations).__name__}); ignoring")
836
- return
861
+ return True
837
862
 
838
863
  if not relations:
839
864
  # Genuinely empty: drop reduced edges anyway, since the
840
865
  # exporter would not have written them either if the graph
841
866
  # had no physical relations. But to be safe, leave them.
842
- return
867
+ return True
843
868
 
844
869
  # 3) Drop the reduced-physical edges produced by the export-side
845
870
  # transitive reduction. They are stale once we re-add the
@@ -913,6 +938,156 @@ class GraphMLImporter:
913
938
  if skipped_missing else "")
914
939
  + (f", skipped {skipped_invalid} (invalid payload)"
915
940
  if skipped_invalid else ""))
941
+ return True
942
+
943
+ def _restore_physical_relations_from_node_attribute(self, tree):
944
+ """Rebuild canonical physical-stratigraphic edges from per-node
945
+ ``physical_relationships`` (d13) packed strings.
946
+
947
+ This is the fallback for GraphML files NOT produced by the
948
+ s3dgraphy lossless exporter — e.g. files hand-authored in yEd
949
+ with the EM 1.6 palette, or files emitted by a pyArchInit-side
950
+ bridge that only fills the per-node attribute. The packed
951
+ string is the same list-of-lists Python-literal format used by
952
+ the pyArchInit ``us_table.rapporti`` column, so the GraphML ↔
953
+ pyArchInit transit is byte-identical when the graph between
954
+ them is unmutated.
955
+
956
+ Behaviour:
957
+ - For each ``<node>`` in the GraphML tree, look for a
958
+ ``<data>`` child whose key id resolves to the
959
+ ``physical_relationships`` (d13) declaration.
960
+ - Parse the string with
961
+ :func:`s3dgraphy.sync.rapporti.parse_rapporti`, which yields
962
+ canonical 5-tuples ``(edge_type, target_us, area, sito,
963
+ swap)``.
964
+ - Resolve target nodes by walking the imported graph's
965
+ nodes-by-name index; drop the reduced-physical edges (same
966
+ way the JSON channel does) before re-adding the canonical
967
+ ones so the in-memory graph isn't doubled up.
968
+
969
+ Called only when
970
+ :meth:`_restore_physical_relations_from_side_channel` reports
971
+ no graph-level JSON channel was present.
972
+ """
973
+ from ..sync.rapporti import parse_rapporti
974
+
975
+ root = tree.getroot()
976
+ ns = 'http://graphml.graphdrawing.org/xmlns'
977
+
978
+ # 1) Resolve the d13 key id from the file (don't assume "d13",
979
+ # since downstream tools may renumber keys on patch).
980
+ d13_key_id = None
981
+ for key_elem in root.findall(f'.//{{{ns}}}key'):
982
+ if (key_elem.attrib.get('attr.name')
983
+ == 'physical_relationships'
984
+ and key_elem.attrib.get('for') == 'node'):
985
+ d13_key_id = key_elem.attrib.get('id')
986
+ break
987
+
988
+ if not d13_key_id:
989
+ # Legacy file with no per-node packed string declaration —
990
+ # the reduced ``is_after`` edges from parse_edges stand.
991
+ return
992
+
993
+ # 2) Gather (source_node_id, raw_string) pairs by EMID. The
994
+ # GraphML node element's ``id`` is the nested layout id; the
995
+ # ``EMID`` (d7) data carries the s3dgraphy UUID, which is what
996
+ # the imported graph indexes by. parse_nodes has already
997
+ # populated the EMID slipback; reuse the same lookup pattern.
998
+ emid_key_id = None
999
+ for key_elem in root.findall(f'.//{{{ns}}}key'):
1000
+ if (key_elem.attrib.get('attr.name') == 'EMID'
1001
+ and key_elem.attrib.get('for') == 'node'):
1002
+ emid_key_id = key_elem.attrib.get('id')
1003
+ break
1004
+
1005
+ per_node_raw: list[tuple[str, str]] = []
1006
+ for node_elem in root.findall(f'.//{{{ns}}}node'):
1007
+ d13 = node_elem.find(f'./{{{ns}}}data[@key="{d13_key_id}"]')
1008
+ if d13 is None or not (d13.text or "").strip():
1009
+ continue
1010
+ emid = None
1011
+ if emid_key_id:
1012
+ emid_data = node_elem.find(
1013
+ f'./{{{ns}}}data[@key="{emid_key_id}"]')
1014
+ if emid_data is not None and emid_data.text:
1015
+ emid = emid_data.text.strip()
1016
+ if not emid:
1017
+ # Fall back to the layout id — process_node_element
1018
+ # uses it as the UUID when EMID is missing.
1019
+ emid = node_elem.attrib.get('id')
1020
+ if emid:
1021
+ per_node_raw.append((emid, d13.text))
1022
+
1023
+ if not per_node_raw:
1024
+ return
1025
+
1026
+ # 3) Build a name → list-of-nodes index for target resolution.
1027
+ # parse_rapporti yields ``target_us`` as the bare unit number
1028
+ # (no "US " prefix); match against the imported nodes' .name
1029
+ # after applying the same strip the writer used.
1030
+ from ..sync.rapporti import strip_us_prefix
1031
+ nodes_by_name: dict = {}
1032
+ for n in self.graph.nodes:
1033
+ key = strip_us_prefix(getattr(n, "name", "") or "")
1034
+ nodes_by_name.setdefault(key, []).append(n)
1035
+
1036
+ # 4) Drop the reduced physical edges (parse_edges produced
1037
+ # them); the per-node packed strings carry the canonical set.
1038
+ before = len(self.graph.edges)
1039
+ self.graph.edges = [
1040
+ e for e in self.graph.edges
1041
+ if e.edge_type not in PHYSICAL_STRATIGRAPHIC_TYPES
1042
+ ]
1043
+ dropped = before - len(self.graph.edges)
1044
+ if hasattr(self.graph, "_indices_dirty"):
1045
+ self.graph._indices_dirty = True
1046
+
1047
+ restored = 0
1048
+ skipped_missing = 0
1049
+ skipped_invalid = 0
1050
+ seen_edge_ids = {e.edge_id for e in self.graph.edges}
1051
+ for source_uuid, raw in per_node_raw:
1052
+ src_node = self.graph.find_node_by_id(source_uuid)
1053
+ if src_node is None:
1054
+ skipped_invalid += 1
1055
+ continue
1056
+ for (edge_type, target_us, _area, _sito, swap) in \
1057
+ parse_rapporti(raw):
1058
+ candidates = nodes_by_name.get(target_us)
1059
+ if not candidates:
1060
+ skipped_missing += 1
1061
+ continue
1062
+ # No family-preference disambiguation here — the
1063
+ # per-node fallback is by definition shallow (no
1064
+ # PD/property targets); first match is fine.
1065
+ tgt_node = candidates[0]
1066
+ src, dst = ((tgt_node, src_node) if swap
1067
+ else (src_node, tgt_node))
1068
+ edge_id = (f"rap_{src.node_id}_{dst.node_id}_"
1069
+ f"{edge_type}")
1070
+ if edge_id in seen_edge_ids:
1071
+ continue
1072
+ try:
1073
+ self.graph.add_edge(edge_id, src.node_id,
1074
+ dst.node_id, edge_type)
1075
+ except ValueError as exc:
1076
+ print(f"[GraphML Parser] WARNING: failed to restore "
1077
+ f"physical edge {src.node_id} "
1078
+ f"-[{edge_type}]-> {dst.node_id}: {exc}")
1079
+ skipped_invalid += 1
1080
+ continue
1081
+ seen_edge_ids.add(edge_id)
1082
+ restored += 1
1083
+
1084
+ print(f"[GraphML Parser] physical_relationships (d13): "
1085
+ f"dropped {dropped} reduced edges, restored {restored} "
1086
+ f"physical edges"
1087
+ + (f", skipped {skipped_missing} (target unresolved)"
1088
+ if skipped_missing else "")
1089
+ + (f", skipped {skipped_invalid} (invalid)"
1090
+ if skipped_invalid else ""))
916
1091
 
917
1092
  def _restore_property_metadata_from_side_channel(self, tree):
918
1093
  """Reapply structured PropertyNode metadata from the