s3dgraphy 1.6.0.dev5__tar.gz → 1.6.0.dev6__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.
- {s3dgraphy-1.6.0.dev5/src/s3dgraphy.egg-info → s3dgraphy-1.6.0.dev6}/PKG-INFO +7 -3
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/pyproject.toml +17 -3
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/em_visual_rules.json +7 -6
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/__init__.py +1 -1
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/node_registry.py +26 -39
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/rdf_exporter.py +86 -18
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/importer/pyarchinit_importer.py +143 -17
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/__init__.py +142 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/_db_handle.py +172 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/_legacy_paradata_svgs.py +264 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/_workspace.py +100 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/conflict_resolver.py +27 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/edge_registry.py +219 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/graph_ingestor.py +1528 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/graph_projector.py +1345 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/graphml_writer.py +2216 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/group_projector.py +214 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/group_store.py +436 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/ingest_result.py +64 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/paradata_store.py +952 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/pyarchinit_pg_importer.py +152 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/uuid7.py +85 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/vocab_provider_core.py +219 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/vocab_types.py +75 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/yed_classifier.py +222 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/yed_detector.py +69 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/yed_group_walker.py +194 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/yed_import_pipeline.py +1519 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/yed_rapporti_policy.py +443 -0
- s3dgraphy-1.6.0.dev6/src/s3dgraphy/sync/yed_table_parser.py +187 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6/src/s3dgraphy.egg-info}/PKG-INFO +7 -3
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy.egg-info/SOURCES.txt +25 -1
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy.egg-info/requires.txt +8 -2
- s3dgraphy-1.6.0.dev6/tests/test_pg_importer.py +307 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/LICENSE +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/MANIFEST.in +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/README.md +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/setup.cfg +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/em.ttl +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/em_document_types.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/em_extractor_types.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/em_palette_icons.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/em_qualia_types.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/em_qualia_types_additions.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/hdto_extension.ttl +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/new_qualia_template.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/outdated_s3dgraphy.ttl +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/outdated_s3dgraphy_nomapping.ttl +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/s3Dgraphy_connections_datamodel.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/s3Dgraphy_connections_datamodel.json.v155.bak +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/s3Dgraphy_node_datamodel.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/RSF.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/RSF.svg +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/SF.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/TSU.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/US.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/USD.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/USVn.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/USVs.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/VSF.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/combiner.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/continuity.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/document.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/extractor.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/property.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/serSU.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/serUSD.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/2D/serUSV.png +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/RSF.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/SF.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/TSU.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/US.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/USD.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/USVn.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/USVs.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/VSF.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/combiner.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/continuity.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/document.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/extractor.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/property.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/serSU.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/serUSD.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/src/3D/serUSV.glb +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/classification.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/data/StratiMiner_Extraction_Prompt.md +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/diagnostics.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/edges/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/edges/connections_loader.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/edges/edge.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/canvas_generator.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/edge_generator.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/epoch_generator.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/graphml_exporter.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/graphml_patcher.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/group_node_generator.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/node_generator.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/palette_resources.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/paradata_generator.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/paradata_image_generator.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/paradata_node_generators.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/table_node_generator.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/utils.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/json_exporter.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/unified_xlsx_exporter.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/graph.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/importer/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/importer/base_importer.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/importer/import_graphml.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/importer/mapped_xlsx_importer.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/importer/qualia_importer.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/importer/unified_xlsx_importer.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/importer/xlsx_importer.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/indices.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/mappings/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/mappings/emdb/generic_specialfind_mapping.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/mappings/emdb/site_properties_mapping.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/mappings/emdb/usm_mapping.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/mappings/generic/excel_to_graphml_mapping.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/mappings/pyarchinit/pyarchinit_us_mapping.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/mappings/registry.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/mappings/template_emdb_mapping.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/mappings/template_pyarchinit_mapping.json +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/merge/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/merge/graph_merger.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/multigraph/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/multigraph/multigraph.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/author_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/base_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/combiner_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/document_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/embargo_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/epoch_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/extractor_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/geo_position_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/graph_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/group_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/hdt_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/license_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/link_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/paradata_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/property_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/representation_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/semantic_shape_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/nodes/stratigraphic_node.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/resolvers/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/resolvers/builtin_rules.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/resolvers/property_resolver.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/templates/em_data_template.xlsx +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/templates/em_palette_template.graphml +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/temporal/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/temporal/inference_engine.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/transforms/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/transforms/aux_tracking.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/transforms/compact.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/transforms/materialize_continuity.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/utils/__init__.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/utils/utils.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/utils/visual_layout.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy.egg-info/dependency_links.txt +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy.egg-info/top_level.txt +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/tests/test_composite_node_name.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/tests/test_filtered_import.py +0 -0
- {s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/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.
|
|
3
|
+
Version: 1.6.0.dev6
|
|
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.
|
|
7
|
+
version = "1.6.0.dev6"
|
|
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]
|
{s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/JSON_config/em_visual_rules.json
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"formal_language": "EM",
|
|
3
|
-
"version": "1.5.
|
|
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.
|
|
137
|
-
"g": 0.
|
|
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": "#
|
|
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.
|
|
177
|
-
"g": 0.
|
|
177
|
+
"r": 0.847,
|
|
178
|
+
"g": 0.392,
|
|
178
179
|
"b": 0.0,
|
|
179
180
|
"a": 1.0
|
|
180
181
|
}
|
{s3dgraphy-1.6.0.dev5 → s3dgraphy-1.6.0.dev6}/src/s3dgraphy/exporter/graphml/node_registry.py
RENAMED
|
@@ -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}.
|
|
244
|
-
"
|
|
245
|
-
"
|
|
246
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
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
|
-
|
|
262
|
-
|
|
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
|
-
|
|
265
|
-
|
|
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
|
-
|
|
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
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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
|
-
|
|
552
|
-
|
|
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
|
|
|
@@ -14,30 +14,100 @@ from ..multigraph.multigraph import multi_graph_manager
|
|
|
14
14
|
|
|
15
15
|
# Conservative SQLite identifier whitelist: letters, digits, underscore.
|
|
16
16
|
# Used to guard table names and filter column names interpolated into
|
|
17
|
-
# query strings (values always go through
|
|
17
|
+
# query strings (values always go through paramstyle binding).
|
|
18
18
|
_SAFE_IDENT_RE = re.compile(r'^[A-Za-z_][A-Za-z0-9_]*$')
|
|
19
19
|
|
|
20
|
+
# Recognized connection URL prefixes for dialect detection.
|
|
21
|
+
_PG_URL_PREFIXES = ("postgresql://", "postgresql+psycopg2://",
|
|
22
|
+
"postgres://")
|
|
23
|
+
_SQLITE_URL_PREFIX = "sqlite:///"
|
|
24
|
+
|
|
25
|
+
|
|
20
26
|
class PyArchInitImporter(BaseImporter):
|
|
21
|
-
def __init__(self, filepath: str
|
|
22
|
-
|
|
27
|
+
def __init__(self, filepath: Optional[str] = None,
|
|
28
|
+
mapping_name: str = None, overwrite: bool = False,
|
|
29
|
+
existing_graph=None,
|
|
30
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
31
|
+
*,
|
|
32
|
+
connection_url: Optional[str] = None):
|
|
23
33
|
"""
|
|
24
34
|
Initialize pyArchInit importer with mapping configuration.
|
|
25
35
|
|
|
26
36
|
Args:
|
|
27
|
-
filepath: Path to
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
filepath: Path to a SQLite database (legacy / default path).
|
|
38
|
+
Mutually exclusive with ``connection_url``. When given,
|
|
39
|
+
it is internally promoted to ``sqlite:///<abspath>`` so
|
|
40
|
+
downstream code uses a single URL-based representation.
|
|
41
|
+
mapping_name: Name of the JSON mapping file to use.
|
|
42
|
+
overwrite: If True, overwrites existing nodes.
|
|
30
43
|
existing_graph: Existing graph instance to use.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
If None, creates new unregistered graph with temporary
|
|
45
|
+
ID. The caller (EM-tools) is responsible for setting
|
|
46
|
+
proper graph_id and registering it in
|
|
47
|
+
MultiGraphManager.
|
|
48
|
+
filters: Optional dict of {column_name: value} to restrict
|
|
49
|
+
the imported rows. Combined with AND. Each column is
|
|
50
|
+
whitelisted against the mapping's column_mappings, then
|
|
51
|
+
bound as a parameterized SQL value — safe against
|
|
52
|
+
injection. Placeholder syntax adapts to the dialect
|
|
53
|
+
(``?`` on SQLite, ``%s`` on PostgreSQL).
|
|
54
|
+
connection_url: SQLAlchemy-style connection URL. Mutually
|
|
55
|
+
exclusive with ``filepath``. Supported schemes:
|
|
56
|
+
``sqlite:///<abspath>``,
|
|
57
|
+
``postgresql://user:pass@host:port/dbname`` (or the
|
|
58
|
+
``postgres://`` alias / ``postgresql+psycopg2://`` form).
|
|
59
|
+
For PostgreSQL, ``psycopg2-binary`` must be installed
|
|
60
|
+
(``pip install s3dgraphy[postgres]``); a friendly
|
|
61
|
+
``ImportError`` fires on first connection attempt if
|
|
62
|
+
it isn't.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
ValueError: If both ``filepath`` and ``connection_url`` are
|
|
66
|
+
given, if neither is given, or if ``connection_url``
|
|
67
|
+
uses an unsupported scheme.
|
|
38
68
|
"""
|
|
69
|
+
# Mutually exclusive + at-least-one validation.
|
|
70
|
+
if filepath is not None and connection_url is not None:
|
|
71
|
+
raise ValueError(
|
|
72
|
+
"Pass either filepath= or connection_url=, not both."
|
|
73
|
+
)
|
|
74
|
+
if filepath is None and connection_url is None:
|
|
75
|
+
raise ValueError(
|
|
76
|
+
"Either filepath= or connection_url= is required."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Resolve dialect + canonical URL + the path-or-URL string we
|
|
80
|
+
# hand to BaseImporter as `filepath` (diagnostic-friendly).
|
|
81
|
+
if filepath is not None:
|
|
82
|
+
abs_path = os.path.abspath(filepath)
|
|
83
|
+
self._dialect = "sqlite"
|
|
84
|
+
self._connection_url = f"{_SQLITE_URL_PREFIX}{abs_path}"
|
|
85
|
+
_base_filepath = filepath
|
|
86
|
+
else:
|
|
87
|
+
if connection_url.startswith(_PG_URL_PREFIXES):
|
|
88
|
+
self._dialect = "postgres"
|
|
89
|
+
elif connection_url.startswith(_SQLITE_URL_PREFIX):
|
|
90
|
+
self._dialect = "sqlite"
|
|
91
|
+
else:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
"Unsupported connection_url scheme: "
|
|
94
|
+
f"{connection_url!r}. "
|
|
95
|
+
"Use sqlite:///<path>, postgresql://..., "
|
|
96
|
+
"or postgres://..."
|
|
97
|
+
)
|
|
98
|
+
self._connection_url = connection_url
|
|
99
|
+
# BaseImporter uses filepath for diagnostics + abspath
|
|
100
|
+
# normalization. SQLite URLs reduce to a real path; PG URLs
|
|
101
|
+
# are passed through verbatim.
|
|
102
|
+
if self._dialect == "sqlite":
|
|
103
|
+
_base_filepath = connection_url[
|
|
104
|
+
len(_SQLITE_URL_PREFIX):
|
|
105
|
+
]
|
|
106
|
+
else:
|
|
107
|
+
_base_filepath = connection_url
|
|
108
|
+
|
|
39
109
|
super().__init__(
|
|
40
|
-
filepath=
|
|
110
|
+
filepath=_base_filepath,
|
|
41
111
|
mapping_name=mapping_name,
|
|
42
112
|
overwrite=overwrite,
|
|
43
113
|
filters=filters,
|
|
@@ -65,6 +135,61 @@ class PyArchInitImporter(BaseImporter):
|
|
|
65
135
|
|
|
66
136
|
self.validate_mapping()
|
|
67
137
|
|
|
138
|
+
# ------------------------------------------------------------------
|
|
139
|
+
# Backend abstraction (#9 multi-backend)
|
|
140
|
+
# ------------------------------------------------------------------
|
|
141
|
+
def _connect(self):
|
|
142
|
+
"""Open a DB-API 2 connection for the active dialect.
|
|
143
|
+
|
|
144
|
+
Returns a connection that the caller must close. For SQLite,
|
|
145
|
+
uses the stdlib ``sqlite3``. For PostgreSQL, uses
|
|
146
|
+
``psycopg2`` and raises a friendly ``ImportError`` if the
|
|
147
|
+
extras are missing.
|
|
148
|
+
"""
|
|
149
|
+
if self._dialect == "sqlite":
|
|
150
|
+
return sqlite3.connect(self.filepath)
|
|
151
|
+
# PostgreSQL path. Probe psycopg2 lazily so SQLite-only
|
|
152
|
+
# callers never pay the import cost (and don't need the wheel).
|
|
153
|
+
try:
|
|
154
|
+
import psycopg2 # noqa: F401
|
|
155
|
+
except ImportError as e: # pragma: no cover
|
|
156
|
+
raise ImportError(
|
|
157
|
+
"PostgreSQL backend requires psycopg2-binary. "
|
|
158
|
+
"Install via: pip install s3dgraphy[postgres]"
|
|
159
|
+
) from e
|
|
160
|
+
import psycopg2
|
|
161
|
+
return psycopg2.connect(self._psycopg2_dsn())
|
|
162
|
+
|
|
163
|
+
def _psycopg2_dsn(self) -> str:
|
|
164
|
+
"""Normalize the connection URL for ``psycopg2.connect()``.
|
|
165
|
+
|
|
166
|
+
``psycopg2`` doesn't understand SQLAlchemy-style driver
|
|
167
|
+
suffixes (e.g. ``postgresql+psycopg2://``): it parses the
|
|
168
|
+
scheme literally and rejects the ``+psycopg2`` part with
|
|
169
|
+
"invalid dsn". The write side of the bridge
|
|
170
|
+
(``s3dgraphy.sync`` via SQLAlchemy) naturally produces those
|
|
171
|
+
URLs, so a caller wiring the *same* connection string into
|
|
172
|
+
both the read side (here) and the write side would otherwise
|
|
173
|
+
hit a silent failure on the read.
|
|
174
|
+
|
|
175
|
+
Stripping the ``+<driver>`` token lets one URL flow into both
|
|
176
|
+
without every caller having to know the dialect-prefix
|
|
177
|
+
convention. ``postgresql+psycopg2://`` → ``postgresql://`` and
|
|
178
|
+
``postgres+psycopg2://`` → ``postgres://`` — both accepted by
|
|
179
|
+
psycopg2. Plain ``postgresql://`` / ``postgres://`` pass
|
|
180
|
+
through untouched.
|
|
181
|
+
"""
|
|
182
|
+
url = self._connection_url
|
|
183
|
+
scheme, sep, rest = url.partition("://")
|
|
184
|
+
if sep and "+" in scheme:
|
|
185
|
+
scheme = scheme.split("+", 1)[0]
|
|
186
|
+
return f"{scheme}://{rest}"
|
|
187
|
+
return url
|
|
188
|
+
|
|
189
|
+
def _qmark(self) -> str:
|
|
190
|
+
"""Parameter placeholder for the active dialect (``?`` / ``%s``)."""
|
|
191
|
+
return "?" if self._dialect == "sqlite" else "%s"
|
|
192
|
+
|
|
68
193
|
def _resolve_node_name(self, row_dict: Dict[str, Any], id_column: str) -> str:
|
|
69
194
|
"""Compose the human-readable node name for ``row_dict``.
|
|
70
195
|
|
|
@@ -296,12 +421,13 @@ class PyArchInitImporter(BaseImporter):
|
|
|
296
421
|
|
|
297
422
|
where_fragments = []
|
|
298
423
|
params: List[Any] = []
|
|
424
|
+
qmark = self._qmark()
|
|
299
425
|
for col, value in self.filters.items():
|
|
300
426
|
# Defense in depth: validate against mapping + ident regex.
|
|
301
427
|
self._validate_filter_column(col)
|
|
302
428
|
if not self._is_safe_identifier(col):
|
|
303
429
|
raise ValueError(f"Unsafe filter column name: {col!r}")
|
|
304
|
-
where_fragments.append(f"{col} =
|
|
430
|
+
where_fragments.append(f"{col} = {qmark}")
|
|
305
431
|
params.append(value)
|
|
306
432
|
|
|
307
433
|
where_clause = " AND ".join(where_fragments)
|
|
@@ -325,7 +451,7 @@ class PyArchInitImporter(BaseImporter):
|
|
|
325
451
|
raise ValueError(f"Unsafe column name: {column!r}")
|
|
326
452
|
table_name = self._get_table_name()
|
|
327
453
|
|
|
328
|
-
conn =
|
|
454
|
+
conn = self._connect()
|
|
329
455
|
try:
|
|
330
456
|
cursor = conn.cursor()
|
|
331
457
|
cursor.execute(
|
|
@@ -340,7 +466,7 @@ class PyArchInitImporter(BaseImporter):
|
|
|
340
466
|
"""Parse pyArchInit database using mapping configuration"""
|
|
341
467
|
try:
|
|
342
468
|
# print("\n=== Starting PyArchInit Import ===")
|
|
343
|
-
conn =
|
|
469
|
+
conn = self._connect()
|
|
344
470
|
cursor = conn.cursor()
|
|
345
471
|
|
|
346
472
|
# Debug del mapping
|