orchestrata_ultimate.domaingen.ttl
Reference › orchestrata_ultimate › domaingen › ttl
ttl renders a DomainModel back to a Turtle ontology (SPEC-105 FR-002 and FR-003). render_ontology writes the prefix header and then, in sorted order and without timestamps, each entity as an OWL class block (_render_class, including required restrictions) followed by the deduplicated datatype properties (_render_datatype_properties) and object properties (_render_object_properties); _esc escapes text. Python types are mapped back to XSD types. Because output is deterministic, parse_ontology from reader applied to the rendered text round-trips the IR structurally (REQ-105-004 and REQ-105-026). The package and constraints import it.
At a glance
| Source | src/orchestrata_ultimate/domaingen/ttl.py |
| Lines of code | 118 |
| Classes | 0 |
| Functions | 5 |
| Imports (internal) | orchestrata_ultimate.core.traceability, orchestrata_ultimate.domaingen.models |
| Imported by | orchestrata_ultimate.domaingen, orchestrata_ultimate.domaingen.constraints |
| Tests that import it | none |
Class diagram
No classes; see Functions.
Dependency diagram
API
Functions
| Function | Signature | Summary |
|---|---|---|
_esc traces |
_esc(text: str) -> str |
|
_render_class traces |
_render_class(ent: Entity) -> str |
Render a single entity as an owl:Class block with required restrictions. |
_render_datatype_properties traces |
_render_datatype_properties(entities: list[Entity]) -> list[str] |
Render the deduplicated, sorted owl:DatatypeProperty declarations. |
_render_object_properties traces |
_render_object_properties(entities: list[Entity]) -> list[str] |
Render the deduplicated, sorted owl:ObjectProperty declarations. |
render_ontology traces |
render_ontology(model: DomainModel) -> str |
Render a DomainModel to a deterministic Turtle ontology. |
Source
"""DomainModel IR -> Turtle ontology (SPEC-105 FR-002/003).
Deterministic: classes and properties are emitted in sorted order with stable
prefixes and no timestamps, so ``parse_ontology(render_ontology(ir))`` round-trips
the IR structurally (REQ-105-004/026).
"""
from __future__ import annotations
from orchestrata_ultimate.core.traceability import traces
from orchestrata_ultimate.domaingen.models import DomainModel, Entity
_PY_TO_XSD = {
"str": "xsd:string",
"int": "xsd:integer",
"float": "xsd:decimal",
"bool": "xsd:boolean",
"datetime": "xsd:dateTime",
}
_PREFIX_HEADER = (
"@prefix owl: <http://www.w3.org/2002/07/owl#> .",
"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .",
)
@traces(spec="105", story="US-105-001", reqs=["REQ-105-003"])
def _esc(text: str) -> str:
return text.replace("\\", "\\\\").replace('"', '\\"').replace("\n", " ")
@traces(spec="105", story="US-105-001", reqs=["REQ-105-003", "REQ-105-026"])
def _render_class(ent: Entity) -> str:
"""Render a single entity as an owl:Class block with required restrictions."""
clauses = [f":{ent.name} a owl:Class"]
if ent.doc:
clauses.append(f' rdfs:comment "{_esc(ent.doc)}"')
if ent.parent:
clauses.append(f" rdfs:subClassOf :{ent.parent}")
for member in sorted(ent.fields, key=lambda f: f.name):
if member.required:
clauses.append(
f" rdfs:subClassOf [ a owl:Restriction ; "
f"owl:onProperty :{member.name} ; owl:minCardinality 1 ]"
)
for rel in sorted(ent.relationships, key=lambda r: r.name):
if rel.required:
clauses.append(
f" rdfs:subClassOf [ a owl:Restriction ; "
f"owl:onProperty :{rel.name} ; owl:minCardinality 1 ]"
)
return " ;\n".join(clauses) + " ."
@traces(spec="105", story="US-105-001", reqs=["REQ-105-003", "REQ-105-026"])
def _render_datatype_properties(entities: list[Entity]) -> list[str]:
"""Render the deduplicated, sorted owl:DatatypeProperty declarations."""
dprops = sorted(
{
(f.name, _PY_TO_XSD.get(f.type, "xsd:string"), e.name)
for e in entities
for f in e.fields
}
)
return [
f":{name} a owl:DatatypeProperty ;\n"
f" rdfs:domain :{domain} ;\n"
f" rdfs:range {xsd_type} ."
for name, xsd_type, domain in dprops
]
@traces(spec="105", story="US-105-001", reqs=["REQ-105-003", "REQ-105-026"])
def _render_object_properties(entities: list[Entity]) -> list[str]:
"""Render the deduplicated, sorted owl:ObjectProperty declarations."""
oprops = sorted(
{
(r.name, e.name, r.target, r.many, r.inverse)
for e in entities
for r in e.relationships
}
)
blocks: list[str] = []
for name, domain, target, many, inverse in oprops:
decl = f":{name} a owl:ObjectProperty"
if not many:
decl += ", owl:FunctionalProperty"
clauses = [decl, f" rdfs:domain :{domain}", f" rdfs:range :{target}"]
if inverse:
clauses.append(f" owl:inverseOf :{inverse}")
blocks.append(" ;\n".join(clauses) + " .")
return blocks
@traces(
spec="105",
story="US-105-001",
reqs=["REQ-105-003", "REQ-105-004", "REQ-105-011", "REQ-105-026"],
)
def render_ontology(model: DomainModel) -> str:
"""Render a DomainModel to a deterministic Turtle ontology."""
out = [f"@prefix : <{model.namespace}> .", *_PREFIX_HEADER, ""]
entities = sorted(model.all_entities(), key=lambda e: e.name)
for ent in entities:
out.append(_render_class(ent))
out.append("")
for block in _render_datatype_properties(entities):
out.append(block)
out.append("")
for block in _render_object_properties(entities):
out.append(block)
out.append("")
return "\n".join(out).rstrip() + "\n"