chore: Initital set of examples for CleverRDFLib #6
@@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE rdf:RDF [
|
||||
<!ENTITY cmns-av "https://www.omg.org/spec/Commons/AnnotationVocabulary/">
|
||||
<!ENTITY cmns-col "https://www.omg.org/spec/Commons/Collections/">
|
||||
<!ENTITY cmns-txt "https://www.omg.org/spec/Commons/TextDatatype/">
|
||||
<!ENTITY dct "http://purl.org/dc/terms/">
|
||||
<!ENTITY owl "http://www.w3.org/2002/07/owl#">
|
||||
<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#">
|
||||
<!ENTITY skos "http://www.w3.org/2004/02/skos/core#">
|
||||
<!ENTITY xsd "http://www.w3.org/2001/XMLSchema#">
|
||||
]>
|
||||
<rdf:RDF xml:base="https://www.omg.org/spec/Commons/Collections/"
|
||||
xmlns:cmns-av="https://www.omg.org/spec/Commons/AnnotationVocabulary/"
|
||||
xmlns:cmns-col="https://www.omg.org/spec/Commons/Collections/"
|
||||
xmlns:cmns-txt="https://www.omg.org/spec/Commons/TextDatatype/"
|
||||
xmlns:dct="http://purl.org/dc/terms/"
|
||||
xmlns:owl="http://www.w3.org/2002/07/owl#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
xmlns:skos="http://www.w3.org/2004/02/skos/core#"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
|
||||
|
||||
<owl:Ontology rdf:about="https://www.omg.org/spec/Commons/Collections/">
|
||||
<rdfs:label>Commons Collections Ontology</rdfs:label>
|
||||
<dct:abstract>The collections ontology defines commonly used concepts for arrangements and schemes for organizing information and collections of things, such as structured collections that may be organized according to some scheme, and related very high level mereology relations to enable association of things with such collections and schemes.</dct:abstract>
|
||||
<dct:contributor>Davide Sottara, Mayo Clinic</dct:contributor>
|
||||
<dct:contributor>Elisa Kendall, Thematix Partners LLC</dct:contributor>
|
||||
<dct:contributor>Pete Rivett, agnos.ai</dct:contributor>
|
||||
<dct:license rdf:datatype="&xsd;anyURI">http://opensource.org/licenses/MIT</dct:license>
|
||||
<owl:imports rdf:resource="https://www.omg.org/spec/Commons/AnnotationVocabulary/"/>
|
||||
<owl:imports rdf:resource="https://www.omg.org/spec/Commons/TextDatatype/"/>
|
||||
<owl:versionIRI rdf:resource="https://www.omg.org/spec/Commons/20221101/Collections/"/>
|
||||
<skos:changeNote>The https://www.omg.org/spec/Commons/20220501/Collections.rdf version of this ontology was modified to better differentiate some of the properties that are subproperties of comprises to aid in user understanding, add new properties that allow for taxonomic parthood, and add a property allowing users to describe the intended method used with respect to arrangement (COMMONS-12).</skos:changeNote>
|
||||
<skos:note>The collections ontology conforms with the OWL 2 DL semantics, and is outside of OWL 2 RL due to the inclusion of a min 0 cardinality constraint. This restriction may be removed as needed to support OWL RL rule-based applications that cannot be extended to support it.</skos:note>
|
||||
<skos:note>This ontology was originally designed for use in the OMG Languages, Countries and Codes (LCC) specification as part of the broader LanguageRepresentation ontology. The concepts have also been used in the Financial Industry Business Ontology (FIBO) for representing collections such as baskets, portfolios records, statistical universes and populations.</skos:note>
|
||||
<cmns-av:copyright>Copyright (c) 2019-2022 Thematix Partners LLC</cmns-av:copyright>
|
||||
<cmns-av:copyright>Copyright (c) 2021-2022 EDM Council, Inc.</cmns-av:copyright>
|
||||
<cmns-av:copyright>Copyright (c) 2021-2022 Mayo Clinic</cmns-av:copyright>
|
||||
<cmns-av:copyright>Copyright (c) 2021-2022 agnos.ai U.K. Ltd</cmns-av:copyright>
|
||||
<cmns-av:copyright>Copyright (c) 2022 Object Management Group, Inc.</cmns-av:copyright>
|
||||
</owl:Ontology>
|
||||
|
||||
<owl:Class rdf:about="&cmns-col;Arrangement">
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="&cmns-col;hasMethod"/>
|
||||
<owl:minCardinality rdf:datatype="&xsd;nonNegativeInteger">0</owl:minCardinality>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label>arrangement</rdfs:label>
|
||||
<skos:definition>systematic plan, manner, or method for making, doing, achieving, or organizing something</skos:definition>
|
||||
<skos:example>Examples include designs, schema, models, methodologies, alphabetical or numeric ordering, and the like.</skos:example>
|
||||
</owl:Class>
|
||||
|
||||
<owl:Class rdf:about="&cmns-col;Collection">
|
||||
<rdfs:label>collection</rdfs:label>
|
||||
<skos:definition>grouping of things (may be zero) that have some shared significance</skos:definition>
|
||||
<cmns-av:usageNote>Users should use either comprises or hasPart, or one of their respective subproperties, to include things in a collection.</cmns-av:usageNote>
|
||||
</owl:Class>
|
||||
|
||||
<owl:Class rdf:about="&cmns-col;Constituent">
|
||||
<rdfs:label>constituent</rdfs:label>
|
||||
<skos:definition>component of a collection or combination of things</skos:definition>
|
||||
<cmns-av:usageNote>Users should use either isIncludedIn or isPartOf, or one of their respective subproperties, to include a constituent in a collection.</cmns-av:usageNote>
|
||||
</owl:Class>
|
||||
|
||||
<owl:Class rdf:about="&cmns-col;StructuredCollection">
|
||||
<rdfs:subClassOf rdf:resource="&cmns-col;Collection"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="&cmns-col;hasArrangement"/>
|
||||
<owl:allValuesFrom rdf:resource="&cmns-col;Arrangement"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label>structured collection</rdfs:label>
|
||||
<skos:definition>collection that has a clearly defined structure or organization</skos:definition>
|
||||
<skos:example>Examples include collections organized thematically, alphabetically, by method used to develop them, according to time and/or version, or based on encoding schemes such as the Dewey Decimal System or Library of Congress Subject Headings.</skos:example>
|
||||
<cmns-av:usageNote>Users should use the hasMethod property on arrangement to describe the methodology for structuring the collection.</cmns-av:usageNote>
|
||||
</owl:Class>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;compliesWith">
|
||||
<rdfs:subPropertyOf rdf:resource="&cmns-col;hasArrangement"/>
|
||||
<rdfs:label>complies with</rdfs:label>
|
||||
<rdfs:range rdf:resource="&cmns-col;Arrangement"/>
|
||||
<skos:definition>adheres to policies or rules specified in</skos:definition>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;comprises">
|
||||
<rdfs:label>comprises</rdfs:label>
|
||||
<skos:definition>includes, consists of, or contains, especially within a particular scope</skos:definition>
|
||||
<skos:note>Note that something can be comprised of something(s) that may or may not be understood as separable parts. In other words, comprises does not imply countability or uniqueness. Whole-part relations are transitive, whereas comprises is not defined to be transitive, so this property is useful in cases where cardinality constraints are needed. comprises and hasPart are not explicitly declared as disjoint to avoid reasoning issues, but should be considered as such.</skos:note>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;hasArrangement">
|
||||
<rdfs:label>has arrangement</rdfs:label>
|
||||
<rdfs:range rdf:resource="&cmns-col;Arrangement"/>
|
||||
<skos:definition>is structured or organized according to</skos:definition>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;hasConstituent">
|
||||
<rdfs:subPropertyOf rdf:resource="&cmns-col;comprises"/>
|
||||
<rdfs:label>has constituent</rdfs:label>
|
||||
<owl:propertyDisjointWith rdf:resource="&cmns-col;hasMember"/>
|
||||
<skos:definition>consists of or contains</skos:definition>
|
||||
<cmns-av:usageNote>This property is disjoint with hasMember, and should be used in cases where the constituents of something are not considered discrete elements of whatever they are included in, such as a substance or composite.</cmns-av:usageNote>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;hasDirectPart">
|
||||
<rdfs:subPropertyOf rdf:resource="&cmns-col;hasPart"/>
|
||||
<rdfs:label>has direct part</rdfs:label>
|
||||
<skos:definition>indicates an immediate 'child' part of something</skos:definition>
|
||||
<cmns-av:usageNote>This property is useful in cases where one is interested in the direct relationships between parts of things, for example to build a tree view.</cmns-av:usageNote>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;hasMember">
|
||||
<rdfs:subPropertyOf rdf:resource="&cmns-col;comprises"/>
|
||||
<rdfs:label>has member</rdfs:label>
|
||||
<skos:definition>includes, as a discrete element</skos:definition>
|
||||
<skos:note>Note that the domain of hasMember should be some sort of collection, aggregate, or group. In the Financial Industry Business Ontology (FIBO), hasMember is used in the case of parties (people and organizations), whereas comprises can have anything in its range.</skos:note>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:DatatypeProperty rdf:about="&cmns-col;hasMethod">
|
||||
<rdfs:subPropertyOf rdf:resource="&cmns-txt;hasTextValue"/>
|
||||
<rdfs:label>has method</rdfs:label>
|
||||
<skos:definition>provides a text description of an approach or method used to accomplish something</skos:definition>
|
||||
<skos:example>This property can be used to describe an arrangement or ordering applied to a collection.</skos:example>
|
||||
</owl:DatatypeProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;hasPart">
|
||||
<rdf:type rdf:resource="&owl;TransitiveProperty"/>
|
||||
<rdfs:label>has part</rdfs:label>
|
||||
<dct:source>Stanford Encyclopedia of Philosophy at http://plato.stanford.edu/entries/mereology/</dct:source>
|
||||
<skos:definition>indicates any portion of something, regardless of whether the portion itself is attached to the remainder or detached; cognitively salient or arbitrarily demarcated; self-connected or disconnected; homogeneous or gerrymandered; material or immaterial; extended or unextended; spatial or temporal</skos:definition>
|
||||
<skos:note>Note that 'has part' is not a subproperty of 'comprises' in order to enable transitivity for whole-part relationships without limiting the use of cardinality constraints on comprises and membership.</skos:note>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;isConstituentOf">
|
||||
<rdfs:subPropertyOf rdf:resource="&cmns-col;isIncludedIn"/>
|
||||
<rdfs:label>is constituent of</rdfs:label>
|
||||
<owl:inverseOf rdf:resource="&cmns-col;hasConstituent"/>
|
||||
<owl:propertyDisjointWith rdf:resource="&cmns-col;isMemberOf"/>
|
||||
<skos:definition>is a component of something else</skos:definition>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;isDirectPartOf">
|
||||
<rdfs:subPropertyOf rdf:resource="&cmns-col;isPartOf"/>
|
||||
<rdfs:label>is direct part of</rdfs:label>
|
||||
<owl:inverseOf rdf:resource="&cmns-col;hasDirectPart"/>
|
||||
<skos:definition>indicates an immediate 'parent' of this part</skos:definition>
|
||||
<cmns-av:usageNote>This property is useful in cases where one is interested in the direct relationships between parts of things, for example to build a tree view.</cmns-av:usageNote>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;isIncludedIn">
|
||||
<rdfs:label>is included in</rdfs:label>
|
||||
<owl:inverseOf rdf:resource="&cmns-col;comprises"/>
|
||||
<skos:definition>is contained in or an element of</skos:definition>
|
||||
<skos:note>Something that is included in something else may be an independently identifiable, discrete element or may be an indistinguishable element once it is combined with the target.</skos:note>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;isMemberOf">
|
||||
<rdfs:subPropertyOf rdf:resource="&cmns-col;isIncludedIn"/>
|
||||
<rdfs:label>is member of</rdfs:label>
|
||||
<owl:inverseOf rdf:resource="&cmns-col;hasMember"/>
|
||||
<skos:definition>is a discrete element of</skos:definition>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
<owl:ObjectProperty rdf:about="&cmns-col;isPartOf">
|
||||
<rdf:type rdf:resource="&owl;TransitiveProperty"/>
|
||||
<rdfs:label>is part of</rdfs:label>
|
||||
<dct:source>Stanford Encyclopedia of Philosophy at http://plato.stanford.edu/entries/mereology/</dct:source>
|
||||
<owl:inverseOf rdf:resource="&cmns-col;hasPart"/>
|
||||
<skos:definition>relates something to another thing that it is some component or portion of, regardless of how that whole-part relationship is manifested</skos:definition>
|
||||
<skos:note>Note that 'is part of' is not a subproperty of 'isIncludedIn' in order to enable transitivity for whole-part relationships without limiting the use of cardinality constraints on inclusion and membership.</skos:note>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
</rdf:RDF>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
Example 01: Load an ontology from its Web URI and load the externally referenced URIs (best-effort)
|
||||
|
||||
Here best-effort means that the example will not fail as long as the main ontology is loaded.
|
||||
That is, if some of the externally referenced URIs fail to load, which will be the case here
|
||||
for all omg.org ontologies, it will not fail the example. It will stil try to load as many
|
||||
external references as it can, besides the main ontology schemaorg.owl.
|
||||
|
||||
The example will go through all the extracted classes and print a summary of their contents.
|
||||
"""
|
||||
|
||||
from cleverrdf_lib.core.loading_result import LoadingResult
|
||||
from cleverrdf_lib.core.ontology_loader import OntologyLoader
|
||||
|
||||
# Create a loader with default configuration
|
||||
loader = OntologyLoader()
|
||||
|
||||
print("Loading Ontology... please wait...")
|
||||
|
||||
# Load an ontology from a local file
|
||||
result: LoadingResult = loader.load_ontology(
|
||||
"https://github.com/schemaorg/schemaorg/raw/refs/heads/main/data/releases/29.4/schemaorg.owl"
|
||||
)
|
||||
|
||||
# Verify loading was successful
|
||||
if not result.is_successful_for_main_ontology:
|
||||
raise RuntimeError(f"Failed to load ontology. Errors: {result.errors}")
|
||||
|
||||
# Verify that at least one ontology was loaded
|
||||
if result.loaded_count == 0:
|
||||
raise RuntimeError("No ontologies were loaded. Check the file path and format.")
|
||||
|
||||
# Access the class hierarchy
|
||||
hierarchy = result.class_hierarchy
|
||||
|
||||
# Navigate the hierarchy
|
||||
for class_node in hierarchy.get_all_classes_as_list():
|
||||
print(f"Class: {class_node.iri}")
|
||||
print(f" Parents: {[p.iri for p in class_node.parents]}")
|
||||
print(f" Children: {[c.iri for c in class_node.children]}")
|
||||
print(f" Object Properties: {[p.iri for p in class_node.object_properties_as_domain.values()]}")
|
||||
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
Example 02: Read ontology from a local file with external references.
|
||||
|
||||
This examples demontrates how to load an ontology from a local file pointed by a relative path.
|
||||
It also enables logging to the STDERR on the console. It also exemplifies how to retrieve a
|
||||
specific class by its IRI.
|
||||
|
||||
This code fails to retrieve the externally referenced OMG.org ontologies, since they do not map
|
||||
to valid and downloadable URIs with the respective ontology.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from frozendict import frozendict
|
||||
|
||||
from cleverrdf_lib.core.class_hierarchy import ClassNode
|
||||
from cleverrdf_lib.core.loading_result import LoadingResult
|
||||
from cleverrdf_lib.core.ontology_loader import OntologyLoader
|
||||
|
||||
logging.basicConfig(
|
||||
level=os.environ.get("PYTHON_LOGGING_LEVEL", logging.INFO),
|
||||
stream=sys.stderr,
|
||||
datefmt="%Y-%m-%dT%H:%M:%S",
|
||||
format=(
|
||||
"%(asctime)s.%(msecs)03d %(process)d %(thread)d %(levelno)03d:%(levelname)-8s "
|
||||
"%(name)-12s %(module)s:%(lineno)s:%(funcName)s %(message)s"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
# Create a loader with default configuration
|
||||
loader = OntologyLoader()
|
||||
|
||||
# Load an ontology from a local file
|
||||
result: LoadingResult = loader.load_ontology("datafiles/schemaorg.owl")
|
||||
|
||||
# Verify loading was successful
|
||||
if not result.is_successful_for_main_ontology:
|
||||
raise RuntimeError(f"Failed to load ontology. Errors: {result.errors}")
|
||||
|
||||
# Verify that at least one ontology was loaded
|
||||
if result.loaded_count == 0:
|
||||
raise RuntimeError("No ontologies were loaded. Check the file path and format.")
|
||||
|
||||
print("\nMAIN Ontology was read successfully, but external ontologies had the following issues:")
|
||||
for error in result.errors:
|
||||
print(f"URI: {error['uri']}")
|
||||
print(f"Message: {error['message']}")
|
||||
if "exception" in error:
|
||||
print(f"Exception: {error['exception']}")
|
||||
if len(result.errors) == 0:
|
||||
print("No errors")
|
||||
|
||||
# Access the class hierarchy
|
||||
hierarchy = result.class_hierarchy
|
||||
|
||||
print("--------- Class Hierarchy built ----------")
|
||||
|
||||
# Navigate the hierarchy
|
||||
classes: frozendict[str, ClassNode] = hierarchy.get_all_classes()
|
||||
|
||||
iri1: str = "https://spec.edmcouncil.org/fibo/ontology/FND/Agreements/Agreements/Agreement"
|
||||
print(f"\nSearching for class with IRI: {iri1}")
|
||||
|
||||
cls1: ClassNode | None = classes.get(iri1)
|
||||
if not cls1:
|
||||
print(f"Class not found: {iri1}")
|
||||
else:
|
||||
print(cls1)
|
||||
|
||||
|
||||
iri2: str = "https://schema.org/PlaceOfWorship"
|
||||
print(f"\nSearching for class with IRI: {iri2}")
|
||||
cls2: ClassNode | None = classes.get(iri2)
|
||||
if not cls2:
|
||||
print(f"Class not found: {iri2}")
|
||||
else:
|
||||
print(cls2)
|
||||
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Example03: Ontology with OMG.org references rewrite
|
||||
|
||||
A IRI to URI Rewriter is demonstrated here that translates omg.org IRIs to the ontology URI.
|
||||
|
||||
This fixes the problem of the previous Example 02, by providing a rewriter that generates
|
||||
proper omg.org URIs for almost all of the externally referenced IRIs.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from urllib.parse import ParseResult, urlparse
|
||||
|
||||
from cleverrdf_lib.core.class_hierarchy import ClassHierarchy, ClassNode
|
||||
from cleverrdf_lib.core.interfaces.iri_2_uri_rewriter import IRI2URIRewriterBaseChain
|
||||
from cleverrdf_lib.core.ontology_loader import OntologyLoader
|
||||
from cleverrdf_lib.resolvers.external_resource_resolver import ExternalResourceResolver
|
||||
from cleverrdf_lib.resolvers.owl_imports_resolver import OWLImportsResolver
|
||||
|
||||
logging.basicConfig(
|
||||
level=os.environ.get("PYTHON_LOGGING_LEVEL", logging.INFO),
|
||||
stream=sys.stderr,
|
||||
datefmt="%Y-%m-%dT%H:%M:%S",
|
||||
format=(
|
||||
"%(asctime)s.%(msecs)03d %(process)d %(thread)d %(levelno)03d:%(levelname)-8s "
|
||||
"%(name)-12s %(module)s:%(lineno)s:%(funcName)s %(message)s"
|
||||
),
|
||||
)
|
||||
|
||||
LOGGER = logging.getLogger("RewriterTest")
|
||||
|
||||
|
||||
resolver1 = ExternalResourceResolver()
|
||||
resolver2 = OWLImportsResolver()
|
||||
resolver1.set_next(resolver2)
|
||||
|
||||
|
||||
class OMGRewriter(IRI2URIRewriterBaseChain):
|
||||
def _can_handle(self, iri_scheme, iri_netloc, iri_path) -> bool:
|
||||
iri_host_path = iri_netloc.split(":")[0] + iri_path
|
||||
return iri_host_path.startswith("www.omg.org/spec") and iri_path.count(".") == 0
|
||||
|
||||
def rewrite_reference(self, iri: str) -> str | None:
|
||||
parsed: ParseResult = urlparse(iri)
|
||||
iri_scheme = parsed.scheme
|
||||
iri_netloc = parsed.netloc
|
||||
iri_path = parsed.path
|
||||
|
||||
# Check if we can handle this IRI
|
||||
if not self._can_handle(iri_scheme, iri_netloc, iri_path):
|
||||
LOGGER.info(f"Cannot handle {iri_path}")
|
||||
return None
|
||||
|
||||
iri_path = iri_path.strip()
|
||||
|
||||
# This IRI is for us, lets rewrite it
|
||||
if iri_path.endswith("/"):
|
||||
#IRI already indicates the ontology
|
||||
uri_path_remapped = iri_path[:-1]
|
||||
uri_path_remapped += ".rdf"
|
||||
else:
|
||||
#IRI indicates a class
|
||||
path_less_last = iri_path.split("/")[:-1]
|
||||
uri_path_remapped = "/".join(path_less_last)
|
||||
uri_path_remapped += ".rdf"
|
||||
return f"{iri_scheme}://{iri_netloc}{uri_path_remapped}"
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "OMG rewriter"
|
||||
|
||||
|
||||
class LoggerRewriter(IRI2URIRewriterBaseChain):
|
||||
def rewrite_reference(self, iri: str) -> str | None:
|
||||
LOGGER.info(f"LOGGERRR IRI: {iri}")
|
||||
return None
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "Logger rewriter"
|
||||
|
||||
|
||||
rewriter1 = OMGRewriter()
|
||||
rewriter2 = LoggerRewriter()
|
||||
|
||||
resolver1.set_rewriters_chain(rewriter1)
|
||||
resolver2.set_rewriters_chain(rewriter1)
|
||||
|
||||
|
||||
loader = OntologyLoader(resolver=resolver1)
|
||||
result = loader.load_ontology(
|
||||
"https://github.com/schemaorg/schemaorg/raw/refs/heads/main/data/releases/29.4/schemaorg.owl"
|
||||
)
|
||||
|
||||
# Check if loading was successful
|
||||
if not result.is_successful_for_main_ontology:
|
||||
print("Loading failed!")
|
||||
for error in result.errors:
|
||||
print(f"URI: {error['uri']}")
|
||||
print(f"Message: {error['message']}")
|
||||
if "exception" in error:
|
||||
print(f"Exception: {error['exception']}")
|
||||
raise RuntimeError("Loading of the main ontology failed! Cannot proceed!")
|
||||
|
||||
print("\nMAIN Ontology was read successfully, but external ontologies had the following issues:")
|
||||
for error in result.errors:
|
||||
print(f"URI: {error['uri']}")
|
||||
print(f"Message: {error['message']}")
|
||||
if "exception" in error:
|
||||
print(f"Exception: {error['exception']}")
|
||||
if len(result.errors) == 0:
|
||||
print("No errors")
|
||||
|
||||
ch: ClassHierarchy = result.class_hierarchy
|
||||
root_classes: list[ClassNode] = ch.get_all_root_classes()
|
||||
print(f"\nFirst root class: {root_classes[0]}")
|
||||
@@ -0,0 +1,103 @@
|
||||
"""
|
||||
Example 05: Use a GraphLoaderRemapperHandler to redirect Web requests to local files.
|
||||
|
||||
This example illustrates how to modify the GraphLoader responsibility chain to
|
||||
insert a new handler that redirects certain URIs to a local file.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from frozendict import frozendict
|
||||
|
||||
from cleverrdf_lib import LoadingResult, OntologyLoader
|
||||
from cleverrdf_lib.core.class_hierarchy import ClassHierarchy, ClassNode
|
||||
from cleverrdf_lib.core.interfaces.interfaces import RemapRule
|
||||
from cleverrdf_lib.utils.graph_loader import GraphLoader
|
||||
from cleverrdf_lib.utils.graph_loader_remapper_handler import GraphLoaderRemapperHandler
|
||||
|
||||
LOGGER: logging.Logger = logging.getLogger("Example04")
|
||||
|
||||
logging.basicConfig(
|
||||
level=os.environ.get("PYTHON_LOGGING_LEVEL", logging.INFO),
|
||||
stream=sys.stderr,
|
||||
datefmt="%Y-%m-%dT%H:%M:%S",
|
||||
format=(
|
||||
"%(asctime)s.%(msecs)03d %(process)d %(thread)d %(levelno)03d:%(levelname)-8s "
|
||||
"%(name)-12s %(module)s:%(lineno)s:%(funcName)s %(message)s"
|
||||
),
|
||||
)
|
||||
|
||||
# Create a mapping of URLs to local files
|
||||
class RemapRule1(RemapRule):
|
||||
def __init__(self):
|
||||
self.url_map = {
|
||||
"https://github.com/schemaorg/schemaorg/raw/refs/heads/main/data/releases/29.4/schemaorg.owl":
|
||||
"datafiles/schemaorg.owl",
|
||||
"https://spec.edmcouncil.org/fibo/ontology/FND/Agreements/Contracts/MutualContractualAgreement":
|
||||
"datafiles/MutualContractualAgreement.owl",
|
||||
"https://www.omg.org/spec/Commons/Collections/":
|
||||
"datafiles/Collections.rdf"
|
||||
}
|
||||
|
||||
def matches(
|
||||
self,
|
||||
uri_scheme: str,
|
||||
uri_netloc: str,
|
||||
uri_path: str,
|
||||
serialization_format: str | None = None,
|
||||
) -> bool:
|
||||
full_uri: str = f"{uri_scheme}://{uri_netloc}{uri_path}"
|
||||
return uri_scheme in ["http", "https"] and full_uri in self.url_map
|
||||
|
||||
def remap(
|
||||
self,
|
||||
uri_scheme: str,
|
||||
uri_netloc: str,
|
||||
uri_path: str,
|
||||
serialization_format: str | None = None,
|
||||
) -> tuple[str, str, str, str | None]:
|
||||
full_uri: str = f"{uri_scheme}://{uri_netloc}{uri_path}"
|
||||
target_path: str = self.url_map[full_uri]
|
||||
path: Path = Path(__file__).resolve().parent / target_path
|
||||
LOGGER.info(f"Remapping {full_uri} to file at {path!s}")
|
||||
return "file", "", str(path), serialization_format
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "Simple file remapper"
|
||||
|
||||
remap_rules: list[RemapRule] = [RemapRule1()]
|
||||
|
||||
# Create redirect handler
|
||||
redirect_handler = GraphLoaderRemapperHandler(remap_rules)
|
||||
|
||||
# Create GraphLoader and insert redirect handler before HTTP handler
|
||||
graph_loader = GraphLoader()
|
||||
graph_loader.insert_handler_in_chain(redirect_handler, position=0)
|
||||
|
||||
# Now HTTP requests will be redirected to local files
|
||||
loader = OntologyLoader(graph_loader=graph_loader)
|
||||
|
||||
result: LoadingResult = loader.load_ontology("https://github.com/schemaorg/schemaorg/raw/refs/heads/main/data/releases/29.4/schemaorg.owl")
|
||||
# This will actually load from ./datafiles/schemaorg.owl
|
||||
|
||||
if not result.is_successful_for_main_ontology:
|
||||
print("Loading failed!")
|
||||
for error in result.errors:
|
||||
print(f"URI: {error['uri']}")
|
||||
print(f"Message: {error['message']}")
|
||||
|
||||
ch: ClassHierarchy = result.class_hierarchy
|
||||
classes: frozendict[str, ClassNode] = ch.get_all_classes()
|
||||
|
||||
iri1: str = "https://www.omg.org/spec/Commons/Collections/StructuredCollection"
|
||||
|
||||
print(f"\nSearching for class with IRI: {iri1}")
|
||||
cls1: ClassNode | None = classes.get(iri1)
|
||||
if not cls1:
|
||||
print(f"Class not found: {iri1}")
|
||||
else:
|
||||
print(cls1)
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Example 05: Use a custom GraphLoader handler to redirect Web requests to local files.
|
||||
|
||||
This example illustrates how to make a custom GraphLoadeHandler, despite in this
|
||||
use case shown, not being so useful especially because there is already the builtin
|
||||
GraphLoaderRemapperHandler.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from frozendict import frozendict
|
||||
from rdflib import Graph
|
||||
|
||||
from cleverrdf_lib import LoadingResult, OntologyLoader
|
||||
from cleverrdf_lib.core.class_hierarchy import ClassHierarchy, ClassNode
|
||||
from cleverrdf_lib.utils.graph_loader import GraphLoader
|
||||
from cleverrdf_lib.utils.graph_loader_base_handler import GraphLoaderBaseHandler
|
||||
|
||||
LOGGER: logging.Logger = logging.getLogger("Example05")
|
||||
|
||||
logging.basicConfig(
|
||||
level=os.environ.get("PYTHON_LOGGING_LEVEL", logging.INFO),
|
||||
stream=sys.stderr,
|
||||
datefmt="%Y-%m-%dT%H:%M:%S",
|
||||
format=(
|
||||
"%(asctime)s.%(msecs)03d %(process)d %(thread)d %(levelno)03d:%(levelname)-8s "
|
||||
"%(name)-12s %(module)s:%(lineno)s:%(funcName)s %(message)s"
|
||||
),
|
||||
)
|
||||
|
||||
class LocalFileRedirectHandler(GraphLoaderBaseHandler):
|
||||
"""Handler that redirects HTTP/HTTPS URLs to local files."""
|
||||
|
||||
def __init__(self, url_to_file_map: dict[str, str]):
|
||||
"""
|
||||
Initialize the redirect handler.
|
||||
|
||||
Args:
|
||||
url_to_file_map: Dictionary mapping URLs to local file paths
|
||||
"""
|
||||
super().__init__()
|
||||
self._url_map = url_to_file_map
|
||||
|
||||
def handle_graph_load(
|
||||
self,
|
||||
uri_scheme: str,
|
||||
uri_netloc: str,
|
||||
uri_path: str,
|
||||
serialization_format: str | None = None,
|
||||
) -> Graph:
|
||||
"""Redirect HTTP/HTTPS URLs to local files if mapped."""
|
||||
# Construct the full URI from components for lookup
|
||||
uri_full = f"{uri_scheme}://{uri_netloc}{uri_path}"
|
||||
|
||||
# Check if this is an HTTP/HTTPS URL that should be redirected
|
||||
if uri_scheme in ("http", "https") and uri_full in self._url_map:
|
||||
# Redirect to local file
|
||||
local_path = self._url_map[uri_full]
|
||||
LOGGER.info(f"Redirecting {uri_full} to {local_path}")
|
||||
graph = Graph()
|
||||
graph.parse(local_path, format=serialization_format)
|
||||
return graph
|
||||
else:
|
||||
# Delegate to next handler (e.g., DefaultGraphLoaderHTTPHandler)
|
||||
return super().handle_graph_load(uri_scheme, uri_netloc, uri_path, serialization_format)
|
||||
|
||||
def get_id(self) -> str:
|
||||
"""Return unique identifier for this handler."""
|
||||
return "local_file_redirect_handler"
|
||||
|
||||
# Assuming LocalFileRedirectHandler is defined as shown in the previous example
|
||||
|
||||
# Create a mapping of URLs to local files
|
||||
url_map = {
|
||||
"https://github.com/schemaorg/schemaorg/raw/refs/heads/main/data/releases/29.4/schemaorg.owl":
|
||||
"datafiles/schemaorg.owl",
|
||||
"https://spec.edmcouncil.org/fibo/ontology/FND/Agreements/Contracts/MutualContractualAgreement":
|
||||
"datafiles/MutualContractualAgreement.owl",
|
||||
"https://www.omg.org/spec/Commons/Collections/":
|
||||
"datafiles/Collections.rdf"
|
||||
}
|
||||
|
||||
# Create redirect handler
|
||||
redirect_handler = LocalFileRedirectHandler(url_map)
|
||||
|
||||
# Create GraphLoader and insert redirect handler before HTTP handler
|
||||
graph_loader = GraphLoader()
|
||||
graph_loader.insert_handler_in_chain(redirect_handler, position=0)
|
||||
|
||||
# Now HTTP requests will be redirected to local files
|
||||
loader = OntologyLoader(graph_loader=graph_loader)
|
||||
|
||||
result: LoadingResult = loader.load_ontology("https://github.com/schemaorg/schemaorg/raw/refs/heads/main/data/releases/29.4/schemaorg.owl")
|
||||
# This will actually load from ./datafiles/schemaorg.owl
|
||||
|
||||
if not result.is_successful_for_main_ontology:
|
||||
print("Loading failed!")
|
||||
for error in result.errors:
|
||||
print(f"URI: {error['uri']}")
|
||||
print(f"Message: {error['message']}")
|
||||
|
||||
ch: ClassHierarchy = result.class_hierarchy
|
||||
classes: frozendict[str, ClassNode] = ch.get_all_classes()
|
||||
|
||||
iri1: str = "https://www.omg.org/spec/Commons/Collections/StructuredCollection"
|
||||
|
||||
print(f"\nSearching for class with IRI: {iri1}")
|
||||
cls1: ClassNode | None = classes.get(iri1)
|
||||
if not cls1:
|
||||
print(f"Class not found: {iri1}")
|
||||
else:
|
||||
print(cls1)
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Example 06: Class Hierarchy converter that creates an alternate representation of the same class hierarchy.
|
||||
|
||||
This is useful when the client code does not need all the class hierarchy information or expects it with
|
||||
a specific data structure. It also shows an example of an ClassBuilderBuildCollector observer for collecting
|
||||
class hierarchy build related warning and error messages.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from cleverrdf_lib import OntologyLoader
|
||||
from cleverrdf_lib.core.class_hierarchy import PropertyType
|
||||
from cleverrdf_lib.core.class_hierarchy_converter_visitor import ClassHierarchyConverterVisitor
|
||||
from cleverrdf_lib.core.interfaces.interfaces import ClassHierarchyConverter
|
||||
from cleverrdf_lib.core.loading_result import LoadingResult
|
||||
from cleverrdf_lib.observers.base_hierarchy_oberver import ClassHierarchyBuildCollector
|
||||
|
||||
logging.basicConfig(
|
||||
level=os.environ.get("PYTHON_LOGGING_LEVEL", logging.INFO),
|
||||
stream=sys.stderr,
|
||||
datefmt="%Y-%m-%dT%H:%M:%S",
|
||||
format=(
|
||||
"%(asctime)s.%(msecs)03d %(process)d %(thread)d %(levelno)03d:%(levelname)-8s "
|
||||
"%(name)-12s %(module)s:%(lineno)s:%(funcName)s %(message)s"
|
||||
),
|
||||
)
|
||||
|
||||
ontology_path: Path = Path(__file__).resolve().parent / "datafiles/schemaorg.owl"
|
||||
|
||||
uri: str = "file://" + str(ontology_path)
|
||||
|
||||
loader = OntologyLoader()
|
||||
result: LoadingResult = loader.load_ontology(uri)
|
||||
print(f"Errors = {result.errors}")
|
||||
print(f"Warnings = {result.warnings}")
|
||||
print(f"Loaded ontologies count: {result.loaded_count}")
|
||||
print(f"Loaded URIs: {result.loaded_uris}")
|
||||
print(f"Merged Graph: {result.merged_graph}")
|
||||
|
||||
print("\nShow up to five loading errors, if there were any...")
|
||||
for error in result.errors[: min(len(result.errors), 5)]:
|
||||
exc = error["exception"]
|
||||
if hasattr(exc, "get_cause"):
|
||||
print(f"{error}, cause: {exc.get_cause()}, trace: ") # type: ignore[attr-defined]
|
||||
else:
|
||||
print(f"{error}, trace: ")
|
||||
if isinstance(exc, BaseException):
|
||||
traceback.print_exception(exc)
|
||||
|
||||
if not result.is_successful_for_main_ontology:
|
||||
print("Loading failed!")
|
||||
for error in result.errors:
|
||||
print(f"URI: {error['uri']}")
|
||||
print(f"Message: {error['message']}")
|
||||
|
||||
build_collector = ClassHierarchyBuildCollector()
|
||||
result.set_class_hierarchy_build_observer(build_collector)
|
||||
|
||||
print("\nClass hierarchy build collector collected the following messages:")
|
||||
print("Build Errors: ")
|
||||
print(build_collector.get_failures())
|
||||
print("Build Warnings: ")
|
||||
print(build_collector.get_warnings())
|
||||
|
||||
class SimpleClassHierarchyConverter(ClassHierarchyConverter):
|
||||
def __init__(self):
|
||||
self._classes_descriptions = {}
|
||||
self._classes_hierarchy = {}
|
||||
self._child_to_parents = {}
|
||||
self._classes = []
|
||||
|
||||
@staticmethod
|
||||
def remove_spaces_and_capitalize(text: str):
|
||||
elems = text.split(" ")
|
||||
result = ""
|
||||
first: bool = True
|
||||
for elem in elems:
|
||||
if first:
|
||||
result += elem
|
||||
first = False
|
||||
else:
|
||||
result += elem.capitalize()
|
||||
return result
|
||||
|
||||
def build_object_property(self, ptype: PropertyType, iri: str, type_iri: str, label: str, comment: str) -> Any:
|
||||
# Ignore properties on purpose, this example only cares about class hierarchy
|
||||
return None
|
||||
|
||||
def build_datatype_property(self, ptype: PropertyType, iri: str, type_iri: str, label: str, comment: str) -> Any:
|
||||
# Ignore properties on purpose, this example only cares about class hierarchy
|
||||
return None
|
||||
|
||||
def assign_object_property(self, new_prop: Any, new_domain: Any, new_range: Any):
|
||||
# Ignore properties on purpose, this example only cares about class hierarchy
|
||||
pass
|
||||
|
||||
def assign_datatype_property(self, new_prop: Any, new_domain: Any, new_range: Any):
|
||||
# Ignore properties on purpose, this example only cares about class hierarchy
|
||||
pass
|
||||
|
||||
def build_datatype(self, iri: str, label: str, comment: str) -> Any:
|
||||
# Ignore datatypes on purpose, this example only cares about class hierarchy
|
||||
return None
|
||||
|
||||
def build_class(self, iri: str, label: str, comment: str) -> Any:
|
||||
new_label = label
|
||||
if not new_label:
|
||||
split_iri = iri.split("#")
|
||||
if len(split_iri) >= 2:
|
||||
new_label = split_iri[1]
|
||||
elif not split_iri[-1].endswith("/"):
|
||||
new_label = split_iri[-1]
|
||||
new_label = new_label[0].lower() + new_label[1:]
|
||||
else:
|
||||
print(f"Unexpected IRI: {iri}, returning as is")
|
||||
new_label = iri
|
||||
|
||||
new_label = self.remove_spaces_and_capitalize(new_label)
|
||||
self._classes.append(new_label)
|
||||
self._classes_descriptions[new_label] = comment
|
||||
|
||||
return new_label
|
||||
|
||||
def add_child_class(self, new_class: Any, new_child_class: Any):
|
||||
class_hierarchy = self._classes_hierarchy.get(new_class, None)
|
||||
if not class_hierarchy:
|
||||
class_hierarchy = []
|
||||
self._classes_hierarchy[new_class] = class_hierarchy
|
||||
class_hierarchy.append(new_child_class)
|
||||
|
||||
def add_parent_class(self, new_class: Any, new_parent_class: Any):
|
||||
child_to_parents = self._child_to_parents.get(new_class, None)
|
||||
if not child_to_parents:
|
||||
child_to_parents = []
|
||||
self._child_to_parents[new_class] = child_to_parents
|
||||
child_to_parents.append(new_parent_class)
|
||||
|
||||
def add_object_property_domain(self, new_class: Any, new_property: Any):
|
||||
pass
|
||||
|
||||
def add_object_property_range(self, new_class: Any, new_property: Any):
|
||||
pass
|
||||
|
||||
def add_datatype_property_domain(self, new_class: Any, new_datatype: Any):
|
||||
pass
|
||||
|
||||
|
||||
converter: SimpleClassHierarchyConverter = SimpleClassHierarchyConverter()
|
||||
|
||||
visitor: ClassHierarchyConverterVisitor = ClassHierarchyConverterVisitor(converter)
|
||||
visitor.visit_classes_from_the_root(result.class_hierarchy)
|
||||
print("\n**********************")
|
||||
print("\n**********************")
|
||||
print("Classes\n")
|
||||
print(converter._classes)
|
||||
print("\n**********************")
|
||||
print("Classes hierarchy\n")
|
||||
print(converter._classes_hierarchy)
|
||||
print("\n**********************")
|
||||
print("Child to parents\n")
|
||||
print(converter._child_to_parents)
|
||||
print("\n**********************")
|
||||
print("Classes descriptions\n")
|
||||
descriptions: list = list(converter._classes_descriptions.items())
|
||||
print(descriptions[:min(len(converter._classes_descriptions), 5)])
|
||||
Reference in New Issue
Block a user