mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
fix(export): normalize entity name to label on all RDF paths
convert_kg_to_rdf() maps an entity's 'name' to 'label'/'text' but was never invoked from export_to_rdf(), so graphs produced by GraphBuilder (which emit 'name') exported with an empty semantica:text on every RDF format (turtle, ntriples, rdfxml, jsonld). Call convert_kg_to_rdf() at the export boundary before validation/serialization so all formats benefit from a single normalization step. Add regression tests asserting a name-only entity exports a non-empty label across all four serializers and the file-writing entry point, plus that an explicit 'text' is not clobbered and an id tail is used as a fallback label. Closes #1097
This commit is contained in:
@@ -1054,6 +1054,12 @@ class RDFExporter:
|
||||
|
||||
self.logger.debug(f"Exporting to RDF format: {format}")
|
||||
|
||||
# Normalize the graph before serialization so every format benefits
|
||||
# from field normalization (e.g. mapping 'name' -> 'label'/'text').
|
||||
# Without this, graphs produced by GraphBuilder (which emit 'name')
|
||||
# export with an empty semantica:text on all RDF paths. See #1097.
|
||||
data = self.serializer.convert_kg_to_rdf(data)
|
||||
|
||||
self.progress_tracker.update_tracking(
|
||||
tracking_id, message="Validating RDF data..."
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import pytest
|
||||
from semantica.export import RDFExporter
|
||||
|
||||
|
||||
RDF_DATA = {
|
||||
"entities": [
|
||||
{"id": "e1", "text": "Apple Inc.", "type": "ORG", "confidence": 0.95},
|
||||
@@ -89,3 +88,74 @@ def test_validate_rdf_returns_overall_valid_key(exporter):
|
||||
result = exporter.validate_rdf(RDF_DATA)
|
||||
assert "overall_valid" in result
|
||||
assert isinstance(result["overall_valid"], bool)
|
||||
|
||||
|
||||
# --- Regression tests for #1097 -------------------------------------------
|
||||
# convert_kg_to_rdf() normalizes an entity's 'name' into 'label'/'text' but was
|
||||
# never called from the export path, so GraphBuilder graphs (which emit 'name')
|
||||
# exported with an empty semantica:text on every RDF format. These tests assert
|
||||
# the human-readable label survives export on all four serializers.
|
||||
|
||||
NAME_ONLY_DATA = {
|
||||
"entities": [
|
||||
{
|
||||
"id": "https://example.org/acme",
|
||||
"name": "Acme Corp",
|
||||
"type": "https://example.org/Org",
|
||||
"confidence": 0.91,
|
||||
},
|
||||
],
|
||||
"relationships": [],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fmt", ["turtle", "ntriples", "rdfxml", "jsonld"])
|
||||
def test_name_only_entity_exports_nonempty_label(exporter, fmt):
|
||||
"""A GraphBuilder-style 'name'-only entity must export a non-empty label.
|
||||
|
||||
Regression for #1097: previously every RDF path dropped the label because
|
||||
convert_kg_to_rdf() was never invoked from export_to_rdf().
|
||||
"""
|
||||
result = exporter.export_to_rdf(NAME_ONLY_DATA, format=fmt)
|
||||
assert "Acme Corp" in result
|
||||
# The empty-text pattern that the bug produced must not appear.
|
||||
assert 'semantica:text ""' not in result
|
||||
|
||||
|
||||
def test_name_only_export_to_file_contains_label(exporter, tmp_path):
|
||||
"""The file-writing entry point must also normalize name -> label (#1097)."""
|
||||
out = tmp_path / "acme.ttl"
|
||||
exporter.export(NAME_ONLY_DATA, str(out), format="turtle")
|
||||
content = out.read_text()
|
||||
assert "Acme Corp" in content
|
||||
assert 'semantica:text ""' not in content
|
||||
|
||||
|
||||
def test_existing_text_not_overwritten_by_name(exporter):
|
||||
"""An entity that already has 'text' must keep it, not be clobbered by 'name' (#1097)."""
|
||||
data = {
|
||||
"entities": [
|
||||
{
|
||||
"id": "https://example.org/acme",
|
||||
"name": "Acme Corp",
|
||||
"text": "Explicit Text",
|
||||
"type": "https://example.org/Org",
|
||||
}
|
||||
],
|
||||
"relationships": [],
|
||||
}
|
||||
result = exporter.export_to_rdf(data, format="turtle")
|
||||
assert "Explicit Text" in result
|
||||
assert "Acme Corp" not in result
|
||||
|
||||
|
||||
def test_id_fallback_label_when_no_name(exporter):
|
||||
"""With neither 'name' nor 'text', the id tail is used as the label (#1097)."""
|
||||
data = {
|
||||
"entities": [
|
||||
{"id": "https://example.org/acme", "type": "https://example.org/Org"}
|
||||
],
|
||||
"relationships": [],
|
||||
}
|
||||
result = exporter.export_to_rdf(data, format="turtle")
|
||||
assert 'semantica:text ""' not in result
|
||||
|
||||
Reference in New Issue
Block a user