s3dgraphy 1.6.0.dev5__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.dev5/src/s3dgraphy.egg-info → s3dgraphy-1.6.0.dev7}/PKG-INFO +7 -3
  2. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/pyproject.toml +17 -3
  3. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_visual_rules.json +7 -6
  4. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/__init__.py +1 -1
  5. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/canvas_generator.py +22 -1
  6. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/graphml_exporter.py +38 -1
  7. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/node_generator.py +33 -3
  8. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/node_registry.py +26 -39
  9. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/rdf_exporter.py +86 -18
  10. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/import_graphml.py +184 -9
  11. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/pyarchinit_importer.py +143 -17
  12. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/__init__.py +142 -0
  13. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/_db_handle.py +172 -0
  14. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/_legacy_paradata_svgs.py +264 -0
  15. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/_workspace.py +100 -0
  16. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/conflict_resolver.py +27 -0
  17. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/edge_registry.py +219 -0
  18. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/graph_ingestor.py +1322 -0
  19. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/graph_projector.py +1332 -0
  20. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/graphml_writer.py +2169 -0
  21. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/group_projector.py +214 -0
  22. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/group_store.py +436 -0
  23. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/ingest_result.py +64 -0
  24. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/paradata_store.py +952 -0
  25. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/pyarchinit_pg_importer.py +152 -0
  26. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/rapporti.py +551 -0
  27. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/uuid7.py +85 -0
  28. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/vocab_provider_core.py +219 -0
  29. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/vocab_types.py +75 -0
  30. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/yed_classifier.py +222 -0
  31. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/yed_detector.py +69 -0
  32. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/yed_group_walker.py +194 -0
  33. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/yed_import_pipeline.py +1519 -0
  34. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/yed_rapporti_policy.py +443 -0
  35. s3dgraphy-1.6.0.dev7/src/s3dgraphy/sync/yed_table_parser.py +187 -0
  36. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/templates/em_palette_template.graphml +11 -0
  37. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7/src/s3dgraphy.egg-info}/PKG-INFO +7 -3
  38. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy.egg-info/SOURCES.txt +26 -1
  39. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy.egg-info/requires.txt +8 -2
  40. s3dgraphy-1.6.0.dev7/tests/test_pg_importer.py +307 -0
  41. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/LICENSE +0 -0
  42. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/MANIFEST.in +0 -0
  43. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/README.md +0 -0
  44. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/setup.cfg +0 -0
  45. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em.ttl +0 -0
  46. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_document_types.json +0 -0
  47. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_extractor_types.json +0 -0
  48. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_palette_icons.json +0 -0
  49. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_qualia_types.json +0 -0
  50. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/em_qualia_types_additions.json +0 -0
  51. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/hdto_extension.ttl +0 -0
  52. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/new_qualia_template.json +0 -0
  53. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/outdated_s3dgraphy.ttl +0 -0
  54. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/outdated_s3dgraphy_nomapping.ttl +0 -0
  55. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/s3Dgraphy_connections_datamodel.json +0 -0
  56. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/s3Dgraphy_connections_datamodel.json.v155.bak +0 -0
  57. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/s3Dgraphy_node_datamodel.json +0 -0
  58. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/RSF.png +0 -0
  59. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/RSF.svg +0 -0
  60. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/SF.png +0 -0
  61. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/TSU.png +0 -0
  62. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/US.png +0 -0
  63. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/USD.png +0 -0
  64. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/USVn.png +0 -0
  65. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/USVs.png +0 -0
  66. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/VSF.png +0 -0
  67. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/combiner.png +0 -0
  68. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/continuity.png +0 -0
  69. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/document.png +0 -0
  70. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/extractor.png +0 -0
  71. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/property.png +0 -0
  72. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/serSU.png +0 -0
  73. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/serUSD.png +0 -0
  74. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/2D/serUSV.png +0 -0
  75. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/RSF.glb +0 -0
  76. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/SF.glb +0 -0
  77. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/TSU.glb +0 -0
  78. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/US.glb +0 -0
  79. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/USD.glb +0 -0
  80. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/USVn.glb +0 -0
  81. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/USVs.glb +0 -0
  82. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/VSF.glb +0 -0
  83. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/combiner.glb +0 -0
  84. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/continuity.glb +0 -0
  85. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/document.glb +0 -0
  86. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/extractor.glb +0 -0
  87. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/property.glb +0 -0
  88. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/serSU.glb +0 -0
  89. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/serUSD.glb +0 -0
  90. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/JSON_config/src/3D/serUSV.glb +0 -0
  91. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/classification.py +0 -0
  92. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/data/StratiMiner_Extraction_Prompt.md +0 -0
  93. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/diagnostics.py +0 -0
  94. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/edges/__init__.py +0 -0
  95. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/edges/connections_loader.py +0 -0
  96. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/edges/edge.py +0 -0
  97. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/__init__.py +0 -0
  98. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/__init__.py +0 -0
  99. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/edge_generator.py +0 -0
  100. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/epoch_generator.py +0 -0
  101. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/graphml_patcher.py +0 -0
  102. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/group_node_generator.py +0 -0
  103. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/palette_resources.py +0 -0
  104. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/paradata_generator.py +0 -0
  105. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/paradata_image_generator.py +0 -0
  106. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/paradata_node_generators.py +0 -0
  107. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/table_node_generator.py +0 -0
  108. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/graphml/utils.py +0 -0
  109. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/json_exporter.py +0 -0
  110. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/exporter/unified_xlsx_exporter.py +0 -0
  111. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/graph.py +0 -0
  112. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/__init__.py +0 -0
  113. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/base_importer.py +0 -0
  114. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/mapped_xlsx_importer.py +0 -0
  115. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/qualia_importer.py +0 -0
  116. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/unified_xlsx_importer.py +0 -0
  117. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/importer/xlsx_importer.py +0 -0
  118. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/indices.py +0 -0
  119. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/__init__.py +0 -0
  120. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/emdb/generic_specialfind_mapping.json +0 -0
  121. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/emdb/site_properties_mapping.json +0 -0
  122. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/emdb/usm_mapping.json +0 -0
  123. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/generic/excel_to_graphml_mapping.json +0 -0
  124. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/pyarchinit/pyarchinit_us_mapping.json +0 -0
  125. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/registry.py +0 -0
  126. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/template_emdb_mapping.json +0 -0
  127. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/mappings/template_pyarchinit_mapping.json +0 -0
  128. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/merge/__init__.py +0 -0
  129. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/merge/graph_merger.py +0 -0
  130. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/multigraph/__init__.py +0 -0
  131. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/multigraph/multigraph.py +0 -0
  132. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/__init__.py +0 -0
  133. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/author_node.py +0 -0
  134. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/base_node.py +0 -0
  135. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/combiner_node.py +0 -0
  136. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/document_node.py +0 -0
  137. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/embargo_node.py +0 -0
  138. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/epoch_node.py +0 -0
  139. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/extractor_node.py +0 -0
  140. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/geo_position_node.py +0 -0
  141. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/graph_node.py +0 -0
  142. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/group_node.py +0 -0
  143. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/hdt_node.py +0 -0
  144. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/license_node.py +0 -0
  145. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/link_node.py +0 -0
  146. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/paradata_node.py +0 -0
  147. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/property_node.py +0 -0
  148. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/representation_node.py +0 -0
  149. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/semantic_shape_node.py +0 -0
  150. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/nodes/stratigraphic_node.py +0 -0
  151. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/resolvers/__init__.py +0 -0
  152. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/resolvers/builtin_rules.py +0 -0
  153. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/resolvers/property_resolver.py +0 -0
  154. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/templates/em_data_template.xlsx +0 -0
  155. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/temporal/__init__.py +0 -0
  156. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/temporal/inference_engine.py +0 -0
  157. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/transforms/__init__.py +0 -0
  158. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/transforms/aux_tracking.py +0 -0
  159. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/transforms/compact.py +0 -0
  160. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/transforms/materialize_continuity.py +0 -0
  161. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/utils/__init__.py +0 -0
  162. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/utils/utils.py +0 -0
  163. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy/utils/visual_layout.py +0 -0
  164. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy.egg-info/dependency_links.txt +0 -0
  165. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/src/s3dgraphy.egg-info/top_level.txt +0 -0
  166. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/tests/test_composite_node_name.py +0 -0
  167. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/tests/test_filtered_import.py +0 -0
  168. {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev7}/tests/test_lossless_roundtrip.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: s3dgraphy
3
- Version: 1.6.0.dev5
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>
@@ -43,6 +43,10 @@ Requires-Dist: pyoxigraph>=0.4; extra == "rdf-embedded"
43
43
  Provides-Extra: visualization
44
44
  Requires-Dist: matplotlib>=3.0; extra == "visualization"
45
45
  Requires-Dist: plotly>=5.0; extra == "visualization"
46
+ Provides-Extra: sync
47
+ Requires-Dist: sqlalchemy>=2.0; extra == "sync"
48
+ Provides-Extra: postgres
49
+ Requires-Dist: psycopg2-binary>=2.9; extra == "postgres"
46
50
  Provides-Extra: dev
47
51
  Requires-Dist: pytest>=7.0; extra == "dev"
48
52
  Requires-Dist: pytest-cov; extra == "dev"
@@ -57,9 +61,9 @@ Requires-Dist: sphinx>=5.0; extra == "docs"
57
61
  Requires-Dist: sphinx-rtd-theme; extra == "docs"
58
62
  Requires-Dist: myst-parser; extra == "docs"
59
63
  Provides-Extra: full
60
- Requires-Dist: s3dgraphy[rdf-embedded,visualization]; extra == "full"
64
+ Requires-Dist: s3dgraphy[rdf-embedded,sync,visualization]; extra == "full"
61
65
  Provides-Extra: all
62
- Requires-Dist: s3dgraphy[dev,docs,full]; extra == "all"
66
+ Requires-Dist: s3dgraphy[dev,docs,full,postgres]; extra == "all"
63
67
  Dynamic: license-file
64
68
 
65
69
  # s3dgraphy
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "s3dgraphy"
7
- version = "1.6.0.dev5"
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"
@@ -92,6 +92,20 @@ visualization = [
92
92
  "matplotlib>=3.0",
93
93
  "plotly>=5.0",
94
94
  ]
95
+ sync = [
96
+ # Backend-agnostic SQL bridge (s3dgraphy.sync) — bidirectional
97
+ # ingestion + projection between a Graph and the host application's
98
+ # SQL tables. SQLite works out of the box; add the [postgres] extra
99
+ # for the PostgreSQL backend. Moved from pyArchInit in 1.6.0
100
+ # (zalmoxes-laran/s3Dgraphy#10).
101
+ "sqlalchemy>=2.0",
102
+ ]
103
+ postgres = [
104
+ # PostgreSQL backend driver. Combine with [sync] for the SQL bridge
105
+ # (zalmoxes-laran/s3Dgraphy#9 + #10). Standalone, it also enables
106
+ # the PG path of PyArchInitImporter when that lands.
107
+ "psycopg2-binary>=2.9",
108
+ ]
95
109
  dev = [
96
110
  "pytest>=7.0",
97
111
  "pytest-cov",
@@ -111,10 +125,10 @@ docs = [
111
125
  # without the dev/docs tooling. The dev/docs/test stacks stay separate
112
126
  # so CI-style installs don't pull editor/QA deps.
113
127
  full = [
114
- "s3dgraphy[rdf-embedded,visualization]",
128
+ "s3dgraphy[rdf-embedded,visualization,sync]",
115
129
  ]
116
130
  all = [
117
- "s3dgraphy[full,dev,docs]",
131
+ "s3dgraphy[full,dev,docs,postgres]",
118
132
  ]
119
133
 
120
134
  [tool.setuptools.packages.find]
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "formal_language": "EM",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "_changelog": {
5
+ "1.5.3": "Fix USD / serUSD material colour: RGB was (0.549, 0.103, 0.0) = #8C1A00 (red), should be #D86400 (orange) to match the canonical USD documentary colour used in the GraphML palette template, node_registry defaults and document_variant_styles.observable. USD border_color also corrected from #8C1A00 to #D86400 (serUSD border was already correct).",
5
6
  "1.5.2": "Added RSF (Reused Special Find / spolia) node style — octagon, red border (#9B3333), white fill. Sibling of SF/VSF, distinguished by border colour. Originating DP: DP-26.",
6
7
  "1.5.1": "Added LocationNodeGroup node style (dashed roundrectangle, kind-based border colour) and is_in_location edge style with primary_modifier override.",
7
8
  "1.5.0": "Initial EM 1.5 visual rules."
@@ -133,13 +134,13 @@
133
134
  "style": {
134
135
  "material": {
135
136
  "color": {
136
- "r": 0.549,
137
- "g": 0.103,
137
+ "r": 0.847,
138
+ "g": 0.392,
138
139
  "b": 0.0,
139
140
  "a": 1.0
140
141
  }
141
142
  },
142
- "border_color": "#8C1A00",
143
+ "border_color": "#D86400",
143
144
  "fill_color": "#F5F5F5",
144
145
  "border_style": "solid",
145
146
  "shape": "rounded_rectangle"
@@ -173,8 +174,8 @@
173
174
  "style": {
174
175
  "material": {
175
176
  "color": {
176
- "r": 0.549,
177
- "g": 0.103,
177
+ "r": 0.847,
178
+ "g": 0.392,
178
179
  "b": 0.0,
179
180
  "a": 1.0
180
181
  }
@@ -1,4 +1,4 @@
1
- __version__ = "1.6.0.dev5"
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')
@@ -236,33 +236,42 @@ class NodeRegistry:
236
236
  )
237
237
 
238
238
  # Sanity-check the canonical stratigraphic types are all present.
239
+ # Caller paths (NodeGenerator, GraphmlPatcher) already fall back to
240
+ # the 'US' stencil — or to inline defaults — when a single type is
241
+ # missing, so we only emit a diagnostic here.
239
242
  missing = sorted(_REQUIRED_PALETTE_TYPES - set(self.visual_properties))
240
243
  for em_type in missing:
241
244
  warnings.warn(
242
245
  f"s3dgraphy: palette template did not yield a stencil "
243
- f"for stratigraphic type {em_type!r}. Falling back to "
244
- "the hardcoded default visual properties. Verify that "
245
- "the palette template still contains the expected "
246
- "NodeLabel (USM01, USM02, USD10, USDxx ellipse for "
247
- "serUSD, USV100, USV102, USV106, SF01, VSF01, RSF01, "
248
- "TSU) or extend node_registry._PALETTE_DISPATCH_RULES.",
246
+ f"for stratigraphic type {em_type!r}. Callers will fall "
247
+ "back to the 'US' stencil. Verify that the palette "
248
+ "template still contains the expected NodeLabel (USM01, "
249
+ "USM02, USD10, USDxx ellipse for serUSD, USV100, USV102, "
250
+ "USV106, SF01, VSF01, RSF01, TSU) or extend "
251
+ "node_registry._PALETTE_DISPATCH_RULES.",
249
252
  S3DgraphyPaletteWarning,
250
253
  stacklevel=2,
251
254
  )
252
- if missing:
253
- # Backfill the missing ones from the hardcoded defaults so
254
- # callers never get None for a known stratigraphic type.
255
- defaults = self._default_visual_properties_dict()
256
- for em_type in missing:
257
- if em_type in defaults:
258
- self.visual_properties[em_type] = defaults[em_type]
259
255
 
260
256
  except (FileNotFoundError, ModuleNotFoundError) as e:
261
- print(f"[s3dgraphy] Warning: Palette template not found: {type(e).__name__}: {e}")
262
- self._load_default_visual_properties()
257
+ # The palette template is bundled with the package — its absence
258
+ # is a packaging/install bug, not a runtime condition we should
259
+ # paper over with hardcoded constants that can silently drift
260
+ # from the canonical palette. Fail loud.
261
+ raise RuntimeError(
262
+ "s3dgraphy: palette template "
263
+ "'templates/em_palette_template.graphml' is missing from "
264
+ f"the installed package ({type(e).__name__}: {e}). "
265
+ "GraphML export cannot proceed without it — reinstall "
266
+ "s3dgraphy or check the package data."
267
+ ) from e
263
268
  except Exception as e:
264
- print(f"[s3dgraphy] Warning: Error loading palette template: {e}")
265
- self._load_default_visual_properties()
269
+ # Same rationale: a corrupt template is a packaging issue, not
270
+ # something to silently swallow.
271
+ raise RuntimeError(
272
+ "s3dgraphy: failed to parse palette template "
273
+ f"'templates/em_palette_template.graphml': {e}"
274
+ ) from e
266
275
 
267
276
  def _extract_visual_properties(self, node_elem: ET.Element, ns: Dict) -> Optional[NodeVisualProperties]:
268
277
  """Extract visual properties from a palette node element."""
@@ -308,28 +317,6 @@ class NodeRegistry:
308
317
  print(f"Warning: Error extracting visual properties: {e}")
309
318
  return None
310
319
 
311
- @staticmethod
312
- def _default_visual_properties_dict() -> Dict[str, NodeVisualProperties]:
313
- """Hardcoded default visual properties (used as fallback)."""
314
- return {
315
- 'US': NodeVisualProperties('rectangle', '#FFFFFF', '#9B3333', 'line', 4.0, '#000000'),
316
- 'USVs': NodeVisualProperties('parallelogram', '#000000', '#248FE7', 'line', 4.0, '#FFFFFF'),
317
- 'USVn': NodeVisualProperties('hexagon', '#000000', '#31792D', 'line', 4.0, '#FFFFFF'),
318
- 'SF': NodeVisualProperties('octagon', '#FFFFFF', '#D8BD30', 'line', 4.0, '#000000'),
319
- 'VSF': NodeVisualProperties('octagon', '#000000', '#B19F61', 'line', 4.0, '#FFFFFF'),
320
- 'RSF': NodeVisualProperties('octagon', '#FFFFFF', '#9B3333', 'line', 4.0, '#000000'),
321
- 'USD': NodeVisualProperties('roundrectangle', '#FFFFFF', '#D86400', 'line', 4.0, '#000000'),
322
- 'serSU': NodeVisualProperties('ellipse', '#FFFFFF', '#9B3333', 'line', 4.0, '#000000'),
323
- 'serUSD': NodeVisualProperties('ellipse', '#FFFFFF', '#D86400', 'line', 4.0, '#000000'),
324
- 'serUSVn': NodeVisualProperties('ellipse', '#000000', '#31792D', 'line', 4.0, '#FFFFFF'),
325
- 'serUSVs': NodeVisualProperties('ellipse', '#000000', '#248FE7', 'line', 4.0, '#FFFFFF'),
326
- 'TSU': NodeVisualProperties('roundrectangle', '#FFFFFF', '#9B3333', 'dashed', 4.0, '#000000'),
327
- }
328
-
329
- def _load_default_visual_properties(self):
330
- """Load hardcoded default visual properties as fallback."""
331
- self.visual_properties = self._default_visual_properties_dict()
332
-
333
320
  def get_visual_properties(self, node_type: str) -> Optional[NodeVisualProperties]:
334
321
  """
335
322
  Get visual properties for a node type.
@@ -259,10 +259,45 @@ class _Datamodel:
259
259
  return _resolve_prefixed(cidoc), None, deprecated
260
260
 
261
261
  def get_qualia_crm_iri(self, property_type: Optional[str]) -> Optional[URIRef]:
262
+ """Resolve a property_type string to its CIDOC class IRI.
263
+
264
+ Lookup strategy (graceful, three steps):
265
+ 1. Exact match against em_qualia_types.json `id` (e.g.
266
+ "absolute_time_start", "height", "color").
267
+ 2. Last segment after dot — handles EM yEd convention where
268
+ properties are labelled with a category prefix
269
+ (e.g. "Dimension.height" → "height", "Spatial.elevation" →
270
+ "elevation").
271
+ 3. Lowercase match — handles minor case mismatches between
272
+ graphml labels and qualia ids (e.g. "Height" → "height").
273
+
274
+ Returns None if no strategy matches; the caller (typically
275
+ ``_compute_primary_iri``) falls back to the generic PropertyNode
276
+ default mapping.
277
+ """
262
278
  if not property_type:
263
279
  return None
280
+ # 1) Exact match
264
281
  crm = self._qualia_class_index.get(property_type)
265
- return _resolve_prefixed(crm)
282
+ if crm:
283
+ return _resolve_prefixed(crm)
284
+ # 2) Last segment after dot (yEd category prefix convention)
285
+ if "." in property_type:
286
+ tail = property_type.rsplit(".", 1)[-1]
287
+ crm = self._qualia_class_index.get(tail)
288
+ if crm:
289
+ return _resolve_prefixed(crm)
290
+ # 3) Lowercase fallback
291
+ crm = self._qualia_class_index.get(property_type.lower())
292
+ if crm:
293
+ return _resolve_prefixed(crm)
294
+ # 4) Combined: lowercase last segment
295
+ if "." in property_type:
296
+ tail_lower = property_type.rsplit(".", 1)[-1].lower()
297
+ crm = self._qualia_class_index.get(tail_lower)
298
+ if crm:
299
+ return _resolve_prefixed(crm)
300
+ return None
266
301
 
267
302
 
268
303
  # ─────────────────────────────────────────────────────────────────────────────
@@ -521,23 +556,44 @@ class RDFExporter:
521
556
  Resolve the rdf:type primary IRI for a node, applying conditional rules.
522
557
 
523
558
  Conditional rule for PropertyNode:
524
- The qualia-type-specific class (looked up in em_qualia_types.json
525
- by `node.property_type`) takes precedence over the generic
526
- PropertyNode default class (typically crm:E54_Dimension).
527
- Without this, an aesthetic_value property would be typed as BOTH
528
- crm:E54_Dimension (PropertyNode default) and crminf:I4_Proposition_Set
529
- (qualia-specific), which is semantically misleading: aesthetic
530
- value is NOT a dimension.
531
-
532
- Fallback for all other node types and for PropertyNode with unknown
533
- property_type: the em_extension.uri or mapping.cidoc declared in the
534
- node datamodel.
559
+ The qualia-type-specific class (looked up in em_qualia_types.json)
560
+ takes precedence over the generic PropertyNode default class
561
+ (typically crm:E54_Dimension). Without this, an aesthetic_value
562
+ property would be typed as BOTH crm:E54_Dimension (PropertyNode
563
+ default) and crminf:I4_Proposition_Set (qualia-specific), which is
564
+ semantically misleading: aesthetic value is NOT a dimension.
565
+
566
+ Lookup key resolution (PropertyNode):
567
+ The s3dgraphy graphml importer preserves raw graphml data
568
+ (``node.name`` carries the NodeLabel, ``node.property_type`` is
569
+ the default "string" unless populated by the
570
+ ``_s3d_property_metadata`` side channel). To enrich at export
571
+ time without burdening the importer with vocabulary knowledge,
572
+ we try the lookup key in this order:
573
+ 1. ``node.property_type`` if explicitly set (not "string")
574
+ 2. ``node.name`` if available (the yEd NodeLabel — qualia
575
+ identifier in EM convention)
576
+ Either string is resolved through the multi-step graceful
577
+ matcher in ``_Datamodel.get_qualia_crm_iri`` (exact / dot-split
578
+ / lowercase). Falls back to the generic node datamodel mapping
579
+ when no qualia term matches (e.g. custom labels like
580
+ "lenght_pipe" stay as em:Qualia + crm:E1_CRM_Entity).
535
581
  """
536
582
  if node_type == "property":
537
583
  ptype = getattr(node, "property_type", None)
538
- qualia_iri = self.datamodel.get_qualia_crm_iri(ptype)
539
- if qualia_iri is not None:
540
- return qualia_iri
584
+ # Treat the default "string" sentinel as "unset" — the importer
585
+ # leaves it on the PropertyNode constructor default when no
586
+ # side-channel metadata is present.
587
+ if ptype and ptype.lower() != "string":
588
+ qualia_iri = self.datamodel.get_qualia_crm_iri(ptype)
589
+ if qualia_iri is not None:
590
+ return qualia_iri
591
+ # Fall back to NodeLabel (em yEd convention: label IS the qualia id)
592
+ name = getattr(node, "name", None)
593
+ if name:
594
+ qualia_iri = self.datamodel.get_qualia_crm_iri(name)
595
+ if qualia_iri is not None:
596
+ return qualia_iri
541
597
  return self.datamodel.get_node_primary_iri(cls_name)
542
598
 
543
599
  def _serialize_type_specific(self, node: Any, node_type: Optional[str],
@@ -547,10 +603,22 @@ class RDFExporter:
547
603
  if node_type == "property":
548
604
  # rdf:type already emitted by _compute_primary_iri (qualia-specific
549
605
  # class takes precedence over PropertyNode default).
606
+ #
607
+ # Value resolution: prefer node.value when set & non-empty;
608
+ # fall back to node.description for legacy graphml where the
609
+ # description data field encodes the value (yEd has no separate
610
+ # "value" socket on annotation-style PropertyNodes).
611
+ raw_value = getattr(node, "value", None)
612
+ if raw_value is None or (isinstance(raw_value, str) and not raw_value.strip()):
613
+ raw_value = getattr(node, "description", None)
614
+ if raw_value is not None and (not isinstance(raw_value, str) or raw_value.strip()):
615
+ ctx.add((node_iri, CRM.P90_has_value, Literal(raw_value)))
616
+
617
+ # Qualia type identifier — same key resolution as _compute_primary_iri:
618
+ # property_type if non-default, otherwise the NodeLabel (name).
550
619
  ptype = getattr(node, "property_type", None)
551
- value = getattr(node, "value", None)
552
- if value is not None:
553
- ctx.add((node_iri, CRM.P90_has_value, Literal(value)))
620
+ if not ptype or ptype.lower() == "string":
621
+ ptype = getattr(node, "name", None)
554
622
  if ptype:
555
623
  ctx.add((node_iri, EM.hasQualiaType, Literal(ptype)))
556
624