mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
Merge pull request #358 from Hawksight-AI/export
fix: resolve TTL export alias failure and add RDF notebook example (#…
This commit is contained in:
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- **TTL Export Alias Fix** (PR #355 by @KaifAhmad1):
|
||||
- Added `_format_aliases` map in `RDFExporter` so `format="ttl"`, `"nt"`, `"xml"`, `"rdf"`, and `"json-ld"` resolve to their canonical counterparts without breaking existing callers
|
||||
- Alias resolution applied at the top of `export_to_rdf()` before format validation — zero public API changes
|
||||
- Added working TTL export cell to `cookbook/introduction/15_Export.ipynb` (Step 3: RDF Export)
|
||||
- Added `tests/export/test_rdf_exporter.py` with 8 tests covering all aliases, canonical formats, error handling, and file export
|
||||
|
||||
- **Incremental/Delta Processing Feature** (PR #349 by @ZohaibHassan16, reviewed and fixed by @KaifAhmad1):
|
||||
- Native delta computation between graph snapshots using SPARQL queries
|
||||
- Delta-aware pipeline execution with `delta_mode` configuration for processing only changed data
|
||||
|
||||
@@ -178,6 +178,13 @@
|
||||
"rdf_exporter.export(kg, \"output.ttl\", format=\"turtle\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": "# TTL alias: format=\"ttl\" is equivalent to format=\"turtle\"\nrdf_data = {\n \"entities\": [\n {\"id\": \"e1\", \"text\": \"Apple Inc.\", \"type\": \"ORG\", \"confidence\": 0.95},\n {\"id\": \"e2\", \"text\": \"Steve Jobs\", \"type\": \"PERSON\", \"confidence\": 0.97},\n ],\n \"relationships\": [\n {\"source_id\": \"e2\", \"target_id\": \"e1\", \"type\": \"founded_by\", \"confidence\": 0.91},\n ],\n}\n\nrdf_exporter.export(rdf_data, \"output.ttl\", format=\"ttl\")\n\nresult = rdf_exporter.validate_rdf(rdf_data)\nprint(f\"Valid: {result['overall_valid']}\")",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
|
||||
@@ -851,6 +851,15 @@ class RDFExporter:
|
||||
# Supported RDF formats
|
||||
self.supported_formats = ["turtle", "rdfxml", "jsonld", "ntriples", "n3"]
|
||||
|
||||
# Format aliases (common extensions/shorthands → canonical names)
|
||||
self._format_aliases = {
|
||||
"ttl": "turtle",
|
||||
"nt": "ntriples",
|
||||
"xml": "rdfxml",
|
||||
"rdf": "rdfxml",
|
||||
"json-ld": "jsonld",
|
||||
}
|
||||
|
||||
# Initialize progress tracker
|
||||
self.progress_tracker = get_progress_tracker()
|
||||
|
||||
@@ -892,6 +901,12 @@ class RDFExporter:
|
||||
)
|
||||
|
||||
try:
|
||||
if not isinstance(format, str):
|
||||
raise ValidationError(
|
||||
f"RDF format must be a string, got: {type(format).__name__}"
|
||||
)
|
||||
fmt = format.strip().lower()
|
||||
format = self._format_aliases.get(fmt, fmt)
|
||||
if format not in self.supported_formats:
|
||||
raise ValidationError(
|
||||
f"Unsupported RDF format: {format}. "
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
"""Tests for RDFExporter format alias resolution (issue #355)."""
|
||||
|
||||
import pytest
|
||||
from semantica.export import RDFExporter
|
||||
|
||||
|
||||
RDF_DATA = {
|
||||
"entities": [
|
||||
{"id": "e1", "text": "Apple Inc.", "type": "ORG", "confidence": 0.95},
|
||||
{"id": "e2", "text": "Steve Jobs", "type": "PERSON", "confidence": 0.97},
|
||||
],
|
||||
"relationships": [
|
||||
{"source_id": "e2", "target_id": "e1", "type": "founded_by", "confidence": 0.91},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def exporter():
|
||||
return RDFExporter()
|
||||
|
||||
|
||||
def test_ttl_alias_produces_same_output_as_turtle(exporter):
|
||||
"""format='ttl' must produce identical output to format='turtle'."""
|
||||
result_turtle = exporter.export_to_rdf(RDF_DATA, format="turtle")
|
||||
result_ttl = exporter.export_to_rdf(RDF_DATA, format="ttl")
|
||||
assert result_ttl == result_turtle
|
||||
|
||||
|
||||
def test_nt_alias_produces_same_output_as_ntriples(exporter):
|
||||
result_canonical = exporter.export_to_rdf(RDF_DATA, format="ntriples")
|
||||
result_alias = exporter.export_to_rdf(RDF_DATA, format="nt")
|
||||
assert result_alias == result_canonical
|
||||
|
||||
|
||||
def test_xml_alias_produces_same_output_as_rdfxml(exporter):
|
||||
result_canonical = exporter.export_to_rdf(RDF_DATA, format="rdfxml")
|
||||
result_alias = exporter.export_to_rdf(RDF_DATA, format="xml")
|
||||
assert result_alias == result_canonical
|
||||
|
||||
|
||||
def test_rdf_alias_produces_same_output_as_rdfxml(exporter):
|
||||
result_canonical = exporter.export_to_rdf(RDF_DATA, format="rdfxml")
|
||||
result_alias = exporter.export_to_rdf(RDF_DATA, format="rdf")
|
||||
assert result_alias == result_canonical
|
||||
|
||||
|
||||
def test_json_ld_alias_produces_same_output_as_jsonld(exporter):
|
||||
result_canonical = exporter.export_to_rdf(RDF_DATA, format="jsonld")
|
||||
result_alias = exporter.export_to_rdf(RDF_DATA, format="json-ld")
|
||||
assert result_alias == result_canonical
|
||||
|
||||
|
||||
def test_canonical_formats_unaffected(exporter):
|
||||
"""Existing canonical format names must continue to work."""
|
||||
# n3 is listed in supported_formats but has no serializer implementation yet
|
||||
for fmt in ("turtle", "rdfxml", "jsonld", "ntriples"):
|
||||
result = exporter.export_to_rdf(RDF_DATA, format=fmt)
|
||||
assert result is not None and len(result) > 0
|
||||
|
||||
|
||||
def test_unsupported_format_raises(exporter):
|
||||
from semantica.utils.exceptions import ValidationError
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
exporter.export_to_rdf(RDF_DATA, format="parquet")
|
||||
|
||||
|
||||
def test_ttl_export_to_file(exporter, tmp_path):
|
||||
out = tmp_path / "output.ttl"
|
||||
exporter.export(RDF_DATA, str(out), format="ttl")
|
||||
assert out.exists()
|
||||
assert out.stat().st_size > 0
|
||||
|
||||
|
||||
def test_non_string_format_raises_validation_error(exporter):
|
||||
"""format=None or non-string must raise ValidationError, not AttributeError."""
|
||||
from semantica.utils.exceptions import ValidationError
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
exporter.export_to_rdf(RDF_DATA, format=None)
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
exporter.export_to_rdf(RDF_DATA, format=123)
|
||||
|
||||
|
||||
def test_validate_rdf_returns_overall_valid_key(exporter):
|
||||
"""validate_rdf() must return 'overall_valid' key (used in notebook example)."""
|
||||
result = exporter.validate_rdf(RDF_DATA)
|
||||
assert "overall_valid" in result
|
||||
assert isinstance(result["overall_valid"], bool)
|
||||
Reference in New Issue
Block a user