diff --git a/Makefile b/Makefile index fa526f6..aacc5ea 100644 --- a/Makefile +++ b/Makefile @@ -56,10 +56,12 @@ all: \ # Do not rebuild the current ontology file if it is already present. It is expected not to change once built. # touch -c: Do not create the file if it does not exist. This will convince the recursive make nothing needs to be done if the file is present. touch -c case_utils/ontology/case-$(case_version).ttl + touch -c case_utils/ontology/case-$(case_version)-subclasses.ttl $(MAKE) \ --directory case_utils/ontology # Confirm the current monolithic file is in place. test -r case_utils/ontology/case-$(case_version).ttl + test -r case_utils/ontology/case-$(case_version)-subclasses.ttl touch $@ check: \ diff --git a/README.md b/README.md index 1367780..e7d036b 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ case_file --disable-hashes sample.txt.json sample.txt Two commands are provided to generate output from a SPARQL query and one or more input graphs. Input graphs can be any graph, such as instance data or supplementary ontology files that supply custom class definitions or other external ontologies. +These commands can be used with any RDF files to run arbitrary SPARQL queries. They have one additional behavior tailored to CASE: If a path query is used for subclasses, the CASE subclass hierarchy will be loaded to supplement the input graph. An expected use case of this feature is subclasses of `ObservableObject`. For instance, if a data graph included an object with only the class `uco-observable:File` specified, the query `?x a/rdfs:subClassOf* uco-observable:ObservableObject` would match `?x` against that object. + #### `case_sparql_construct` diff --git a/case_utils/case_file/__init__.py b/case_utils/case_file/__init__.py index a25481d..3ac56ac 100644 --- a/case_utils/case_file/__init__.py +++ b/case_utils/case_file/__init__.py @@ -85,7 +85,7 @@ def create_file_node( graph.add(( n_file, NS_RDF.type, - NS_UCO_OBSERVABLE.ObservableObject + NS_UCO_OBSERVABLE.File )) basename = os.path.basename(filepath) diff --git a/case_utils/case_sparql_construct/__init__.py b/case_utils/case_sparql_construct/__init__.py index f53df06..abcdd27 100644 --- a/case_utils/case_sparql_construct/__init__.py +++ b/case_utils/case_sparql_construct/__init__.py @@ -18,28 +18,52 @@ __version__ = "0.1.0" import argparse -import os import logging +import os +import sys import typing import rdflib.plugins.sparql # type: ignore -import case_utils +import case_utils.ontology + +from case_utils.ontology.version_info import * _logger = logging.getLogger(os.path.basename(__file__)) def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument("-d", "--debug", action="store_true") - parser.add_argument("--disallow-empty-results", action="store_true", help="Raise error if no results are returned for query.") - parser.add_argument("--output-format", help="Override extension-based format guesser.") + + # Configure debug logging before running parse_args, because there could be an error raised before the construction of the argument parser. + logging.basicConfig(level=logging.DEBUG if ("--debug" in sys.argv or "-d" in sys.argv) else logging.INFO) + + built_version_choices_list = ["none", "case-" + CURRENT_CASE_VERSION] + + parser.add_argument( + "-d", + "--debug", + action="store_true" + ) + parser.add_argument( + "--built-version", + choices=tuple(built_version_choices_list), + default="case-"+CURRENT_CASE_VERSION, + help="Ontology version to use to supplement query, such as for subclass querying. Does not require networking to use. Default is most recent CASE release." + ) + parser.add_argument( + "--disallow-empty-results", + action="store_true", + help="Raise error if no results are returned for query." + ) + parser.add_argument( + "--output-format", + help="Override extension-based format guesser." + ) parser.add_argument("out_graph") parser.add_argument("in_sparql") parser.add_argument("in_graph", nargs="+") args = parser.parse_args() - logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO) - in_graph = rdflib.Graph() for in_graph_filename in args.in_graph: in_graph.parse(in_graph_filename) @@ -58,6 +82,9 @@ def main() -> None: construct_query_text = in_fh.read().strip() assert not construct_query_text is None + if "subClassOf" in construct_query_text: + case_utils.ontology.load_subclass_hierarchy(in_graph, built_version=args.built_version) + construct_query_object = rdflib.plugins.sparql.prepareQuery(construct_query_text, initNs=nsdict) # https://rdfextras.readthedocs.io/en/latest/working_with.html diff --git a/case_utils/case_sparql_select/__init__.py b/case_utils/case_sparql_select/__init__.py index 7bbb809..12404f1 100644 --- a/case_utils/case_sparql_select/__init__.py +++ b/case_utils/case_sparql_select/__init__.py @@ -30,13 +30,17 @@ import argparse import binascii +import importlib.resources import logging import os +import sys import pandas as pd # type: ignore import rdflib.plugins.sparql # type: ignore -import case_utils +import case_utils.ontology + +from case_utils.ontology.version_info import * NS_XSD = rdflib.XSD @@ -44,15 +48,36 @@ def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument("-d", "--debug", action="store_true") - parser.add_argument("--disallow-empty-results", action="store_true", help="Raise error if no results are returned for query.") - parser.add_argument("out_table", help="Expected extensions are .html for HTML tables or .md for Markdown tables.") + + # Configure debug logging before running parse_args, because there could be an error raised before the construction of the argument parser. + logging.basicConfig(level=logging.DEBUG if ("--debug" in sys.argv or "-d" in sys.argv) else logging.INFO) + + built_version_choices_list = ["none", "case-" + CURRENT_CASE_VERSION] + + parser.add_argument( + "-d", + "--debug", + action="store_true" + ) + parser.add_argument( + "--built-version", + choices=tuple(built_version_choices_list), + default="case-"+CURRENT_CASE_VERSION, + help="Ontology version to use to supplement query, such as for subclass querying. Does not require networking to use. Default is most recent CASE release." + ) + parser.add_argument( + "--disallow-empty-results", + action="store_true", + help="Raise error if no results are returned for query." + ) + parser.add_argument( + "out_table", + help="Expected extensions are .html for HTML tables or .md for Markdown tables." + ) parser.add_argument("in_sparql") parser.add_argument("in_graph", nargs="+") args = parser.parse_args() - logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO) - graph = rdflib.Graph() for in_graph_filename in args.in_graph: graph.parse(in_graph_filename) @@ -65,6 +90,9 @@ def main() -> None: select_query_text = in_fh.read().strip() _logger.debug("select_query_text = %r." % select_query_text) + if "subClassOf" in select_query_text: + case_utils.ontology.load_subclass_hierarchy(graph, built_version=args.built_version) + # Build columns list from SELECT line. select_query_text_lines = select_query_text.split("\n") select_line = [line for line in select_query_text_lines if line.startswith("SELECT ")][0] diff --git a/case_utils/ontology/Makefile b/case_utils/ontology/Makefile index bd53acb..ab4e5db 100644 --- a/case_utils/ontology/Makefile +++ b/case_utils/ontology/Makefile @@ -24,6 +24,9 @@ RDF_TOOLKIT_JAR := $(case_srcdir)/lib/rdf-toolkit.jar case_version := $(shell python3 version_info.py) all: \ + case-$(case_version)-subclasses.ttl + +.PRECIOUS: \ case-$(case_version).ttl case-$(case_version).ttl: \ @@ -45,5 +48,34 @@ case-$(case_version).ttl: \ --target-format turtle mv _$@ $@ +case-$(case_version)-subclasses.ttl: \ + case-$(case_version).ttl \ + src/subclasses_ttl.py + # The CASE ontology test venv is made by the earlier build step + # of case_monolithic.ttl. However, unless a new ontology + # release is being made, that step will have been skipped. + # This recursive Make call guarantees the virtual environment is + # set up. + $(MAKE) \ + --directory $(case_srcdir)/tests \ + .venv.done.log + #TODO This cleanup step should be removed after the 0.3.0 release of CASE-Utility-SHACL-Inheritance-Reviewer. + test ! -d $(uco_srcdir)/dependencies/CASE-Utility-SHACL-Inheritance-Reviewer/build \ + || rm -rf \ + $(uco_srcdir)/dependencies/CASE-Utility-SHACL-Inheritance-Reviewer/build + source $(case_srcdir)/tests/venv/bin/activate \ + && python3 src/subclasses_ttl.py \ + __$@ \ + $< + java -jar $(RDF_TOOLKIT_JAR) \ + --inline-blank-nodes \ + --source __$@ \ + --source-format turtle \ + --target _$@ \ + --target-format turtle + rm __$@ + mv _$@ $@ + clean: - @rm -f case-$(case_version).ttl + @rm -f \ + case-$(case_version)*.ttl diff --git a/case_utils/ontology/__init__.py b/case_utils/ontology/__init__.py index 081745d..eb14af5 100644 --- a/case_utils/ontology/__init__.py +++ b/case_utils/ontology/__init__.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + # This software was developed at the National Institute of Standards # and Technology by employees of the Federal Government in the course # of their official duties. Pursuant to title 17 Section 105 of the @@ -8,3 +10,33 @@ # reliability, or any other characteristic. # # We would appreciate acknowledgement if the software is used. + +__version__ = "0.1.0" + +import importlib.resources +import logging +import os + +import rdflib + +# Yes, this next import is self-referential (/circular). But, it does work with importlib. +import case_utils.ontology + +from .version_info import * + +_logger = logging.getLogger(os.path.basename(__file__)) + +def load_subclass_hierarchy( + graph : rdflib.Graph, + *, + built_version : str = "case-"+CURRENT_CASE_VERSION +) -> None: + """ + Adds all ontology rdfs:subClassOf statements from the version referred to by built_version. + """ + if built_version != "none": + _logger.debug("Loading subclass hierarchy.") + ttl_filename = built_version + "-subclasses.ttl" + _logger.debug("ttl_filename = %r.", ttl_filename) + ttl_data = importlib.resources.read_text(case_utils.ontology, ttl_filename) + graph.parse(data=ttl_data) diff --git a/case_utils/ontology/case-0.5.0-subclasses.ttl b/case_utils/ontology/case-0.5.0-subclasses.ttl new file mode 100644 index 0000000..15da4a3 --- /dev/null +++ b/case_utils/ontology/case-0.5.0-subclasses.ttl @@ -0,0 +1,1568 @@ +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix xs: . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf + , + + ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf + , + + ; + . + + + rdfs:subClassOf + , + + ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf + , + + ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf + , + + ; + . + + + rdfs:subClassOf + , + + ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf + , + + ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf + , + + ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf + , + + ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + + + rdfs:subClassOf rdfs:Resource ; + . + diff --git a/case_utils/ontology/src/README.md b/case_utils/ontology/src/README.md new file mode 100644 index 0000000..6068cc0 --- /dev/null +++ b/case_utils/ontology/src/README.md @@ -0,0 +1 @@ +This directory is not intended to be exported as part of the `case_utils` package. diff --git a/case_utils/ontology/src/subclasses_ttl.py b/case_utils/ontology/src/subclasses_ttl.py new file mode 100644 index 0000000..893d458 --- /dev/null +++ b/case_utils/ontology/src/subclasses_ttl.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +# This software was developed at the National Institute of Standards +# and Technology by employees of the Federal Government in the course +# of their official duties. Pursuant to title 17 Section 105 of the +# United States Code this software is not subject to copyright +# protection and is in the public domain. NIST assumes no +# responsibility whatsoever for its use by other parties, and makes +# no guarantees, expressed or implied, about its quality, +# reliability, or any other characteristic. +# +# We would appreciate acknowledgement if the software is used. + +""" +This script creates an excerpt of an ontology graph that consists solely of all rdfs:subClassOf statements. +""" + +__version__ = "0.1.0" + +import argparse + +import rdflib + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("out_ttl") + parser.add_argument("in_ttl", nargs="+") + args = parser.parse_args() + + in_graph = rdflib.Graph() + out_graph = rdflib.Graph() + + in_ttl : str + for in_ttl in args.in_ttl: + in_graph.parse(in_ttl) + + for triple in in_graph.triples(( + None, + rdflib.RDFS.subClassOf, + None + )): + out_graph.add(triple) + + out_graph.serialize(args.out_ttl) + +if __name__ == "__main__": + main() diff --git a/tests/case_utils/case_file/kb.json b/tests/case_utils/case_file/kb.json index 5f3863b..eadf906 100644 --- a/tests/case_utils/case_file/kb.json +++ b/tests/case_utils/case_file/kb.json @@ -10,7 +10,7 @@ "@graph": [ { "@id": "kb:file-69751792-9d04-5f5f-8791-99ca3729cd3c", - "@type": "uco-observable:ObservableObject", + "@type": "uco-observable:File", "uco-core:hasFacet": { "@type": "uco-observable:FileFacet", "uco-observable:fileName": "sample.txt", @@ -26,7 +26,7 @@ }, { "@id": "kb:file-9477a6eb-3a94-590b-b51e-6ce6892f0941", - "@type": "uco-observable:ObservableObject", + "@type": "uco-observable:File", "uco-core:hasFacet": [ { "@type": "uco-observable:ContentDataFacet", diff --git a/tests/case_utils/case_file/kb.ttl b/tests/case_utils/case_file/kb.ttl index 0fb50ca..f82b0f3 100644 --- a/tests/case_utils/case_file/kb.ttl +++ b/tests/case_utils/case_file/kb.ttl @@ -9,7 +9,7 @@ @prefix xsd: . kb:file-69751792-9d04-5f5f-8791-99ca3729cd3c - a uco-observable:ObservableObject ; + a uco-observable:File ; uco-core:hasFacet [ a uco-observable:FileFacet ; uco-observable:fileName "sample.txt" ; @@ -19,7 +19,7 @@ kb:file-69751792-9d04-5f5f-8791-99ca3729cd3c . kb:file-9477a6eb-3a94-590b-b51e-6ce6892f0941 - a uco-observable:ObservableObject ; + a uco-observable:File ; uco-core:hasFacet [ a uco-observable:ContentDataFacet ; diff --git a/tests/case_utils/case_file/sample.txt-disable_hashes.ttl b/tests/case_utils/case_file/sample.txt-disable_hashes.ttl index 0bc4d82..2b526be 100644 --- a/tests/case_utils/case_file/sample.txt-disable_hashes.ttl +++ b/tests/case_utils/case_file/sample.txt-disable_hashes.ttl @@ -7,7 +7,7 @@ @prefix xsd: . kb:file-69751792-9d04-5f5f-8791-99ca3729cd3c - a uco-observable:ObservableObject ; + a uco-observable:File ; uco-core:hasFacet [ a uco-observable:FileFacet ; uco-observable:fileName "sample.txt" ; diff --git a/tests/case_utils/case_file/sample.txt-nocompact.json b/tests/case_utils/case_file/sample.txt-nocompact.json index 6d2569b..c14fdf3 100644 --- a/tests/case_utils/case_file/sample.txt-nocompact.json +++ b/tests/case_utils/case_file/sample.txt-nocompact.json @@ -13,18 +13,18 @@ "@graph": [ { "@id": "http://example.org/kb/file-4216a9f3-45fc-55aa-8e48-a9f828173625", - "@type": "https://unifiedcyberontology.org/ontology/uco/observable#ObservableObject", + "@type": "https://unifiedcyberontology.org/ontology/uco/observable#File", "https://unifiedcyberontology.org/ontology/uco/core#hasFacet": [ { - "@id": "_:Ncbd74ec540fa42b09802cdf033ec0e86" + "@id": "_:N44ce7f76801a429d9aabefe1046da060" }, { - "@id": "_:N1b091168b6e746ca8ffef67ba546f52c" + "@id": "_:Ndd6e8785860848aeb6823b493c6420a7" } ] }, { - "@id": "_:Ncbd74ec540fa42b09802cdf033ec0e86", + "@id": "_:N44ce7f76801a429d9aabefe1046da060", "@type": "https://unifiedcyberontology.org/ontology/uco/observable#FileFacet", "https://unifiedcyberontology.org/ontology/uco/observable#fileName": "sample.txt", "https://unifiedcyberontology.org/ontology/uco/observable#modifiedTime": { @@ -34,26 +34,26 @@ "https://unifiedcyberontology.org/ontology/uco/observable#sizeInBytes": 4 }, { - "@id": "_:N1b091168b6e746ca8ffef67ba546f52c", + "@id": "_:Ndd6e8785860848aeb6823b493c6420a7", "@type": "https://unifiedcyberontology.org/ontology/uco/observable#ContentDataFacet", "https://unifiedcyberontology.org/ontology/uco/observable#hash": [ { - "@id": "_:Nf0f6ff73c49045ffa53e2fc83249d20b" + "@id": "_:N334acc4a58624aa29968e0d155bd34c7" }, { - "@id": "_:Na2470e1f29914026a1690b0e9386cc1f" + "@id": "_:Na1176e4f42e442b2bd4167202aa93383" }, { - "@id": "_:Nd8473ff30e87456d8dcd034729ba27ed" + "@id": "_:N400f582d54af4f8190253677f57de728" }, { - "@id": "_:Ncaa10f1e5cf84d8286f1d1c536b9fa06" + "@id": "_:Nef2949777f2b4b34a46a7d99ea01f44e" } ], "https://unifiedcyberontology.org/ontology/uco/observable#sizeInBytes": 4 }, { - "@id": "_:Nf0f6ff73c49045ffa53e2fc83249d20b", + "@id": "_:N334acc4a58624aa29968e0d155bd34c7", "@type": "https://unifiedcyberontology.org/ontology/uco/types#Hash", "https://unifiedcyberontology.org/ontology/uco/types#hashMethod": { "@type": "https://unifiedcyberontology.org/ontology/uco/vocabulary#HashNameVocab", @@ -65,7 +65,7 @@ } }, { - "@id": "_:Na2470e1f29914026a1690b0e9386cc1f", + "@id": "_:Na1176e4f42e442b2bd4167202aa93383", "@type": "https://unifiedcyberontology.org/ontology/uco/types#Hash", "https://unifiedcyberontology.org/ontology/uco/types#hashMethod": { "@type": "https://unifiedcyberontology.org/ontology/uco/vocabulary#HashNameVocab", @@ -77,7 +77,7 @@ } }, { - "@id": "_:Nd8473ff30e87456d8dcd034729ba27ed", + "@id": "_:N400f582d54af4f8190253677f57de728", "@type": "https://unifiedcyberontology.org/ontology/uco/types#Hash", "https://unifiedcyberontology.org/ontology/uco/types#hashMethod": { "@type": "https://unifiedcyberontology.org/ontology/uco/vocabulary#HashNameVocab", @@ -89,7 +89,7 @@ } }, { - "@id": "_:Ncaa10f1e5cf84d8286f1d1c536b9fa06", + "@id": "_:Nef2949777f2b4b34a46a7d99ea01f44e", "@type": "https://unifiedcyberontology.org/ontology/uco/types#Hash", "https://unifiedcyberontology.org/ontology/uco/types#hashMethod": { "@type": "https://unifiedcyberontology.org/ontology/uco/vocabulary#HashNameVocab", diff --git a/tests/case_utils/case_file/sample.txt.json b/tests/case_utils/case_file/sample.txt.json index c2b4f6e..f9b1b2d 100644 --- a/tests/case_utils/case_file/sample.txt.json +++ b/tests/case_utils/case_file/sample.txt.json @@ -13,18 +13,18 @@ "@graph": [ { "@id": "kb:file-a44b2ee3-968f-5528-883a-fa65ac45ce2c", - "@type": "uco-observable:ObservableObject", + "@type": "uco-observable:File", "uco-core:hasFacet": [ { - "@id": "_:N4acc005af38245cb843d4a7009cce7df" + "@id": "_:N2767c0726ce542be9a1ad27b1f7ae944" }, { - "@id": "_:Nc02deac289534fbf93c061d03bbb589d" + "@id": "_:N78869d50bb384b24b8fd147d9bfe7e51" } ] }, { - "@id": "_:N4acc005af38245cb843d4a7009cce7df", + "@id": "_:N2767c0726ce542be9a1ad27b1f7ae944", "@type": "uco-observable:FileFacet", "uco-observable:fileName": "sample.txt", "uco-observable:modifiedTime": { @@ -34,26 +34,26 @@ "uco-observable:sizeInBytes": 4 }, { - "@id": "_:Nc02deac289534fbf93c061d03bbb589d", + "@id": "_:N78869d50bb384b24b8fd147d9bfe7e51", "@type": "uco-observable:ContentDataFacet", "uco-observable:hash": [ { - "@id": "_:Nbbf71d61d9fa4615bc074268fd60b4b5" + "@id": "_:N6407c0f0a27545e0a15a7e147e862003" }, { - "@id": "_:N6741cf44d9da464191c6a0014dbb602b" + "@id": "_:Na90e92d493a84c78802a307bbbdaafda" }, { - "@id": "_:N3b6cc89816394294bc260958408d74de" + "@id": "_:Nfe3e54d32dcc4ef79934ca120dc3d59e" }, { - "@id": "_:Nfc008212dd584b16a8d4d50df33f0a47" + "@id": "_:N80c90d89e47b4f30b585752201376e22" } ], "uco-observable:sizeInBytes": 4 }, { - "@id": "_:Nbbf71d61d9fa4615bc074268fd60b4b5", + "@id": "_:N6407c0f0a27545e0a15a7e147e862003", "@type": "uco-types:Hash", "uco-types:hashMethod": { "@type": "uco-vocabulary:HashNameVocab", @@ -65,7 +65,7 @@ } }, { - "@id": "_:N6741cf44d9da464191c6a0014dbb602b", + "@id": "_:Na90e92d493a84c78802a307bbbdaafda", "@type": "uco-types:Hash", "uco-types:hashMethod": { "@type": "uco-vocabulary:HashNameVocab", @@ -77,7 +77,7 @@ } }, { - "@id": "_:N3b6cc89816394294bc260958408d74de", + "@id": "_:Nfe3e54d32dcc4ef79934ca120dc3d59e", "@type": "uco-types:Hash", "uco-types:hashMethod": { "@type": "uco-vocabulary:HashNameVocab", @@ -89,7 +89,7 @@ } }, { - "@id": "_:Nfc008212dd584b16a8d4d50df33f0a47", + "@id": "_:N80c90d89e47b4f30b585752201376e22", "@type": "uco-types:Hash", "uco-types:hashMethod": { "@type": "uco-vocabulary:HashNameVocab", diff --git a/tests/case_utils/case_file/sample.txt.ttl b/tests/case_utils/case_file/sample.txt.ttl index 16029aa..faecc50 100644 --- a/tests/case_utils/case_file/sample.txt.ttl +++ b/tests/case_utils/case_file/sample.txt.ttl @@ -9,7 +9,7 @@ @prefix xsd: . kb:file-9477a6eb-3a94-590b-b51e-6ce6892f0941 - a uco-observable:ObservableObject ; + a uco-observable:File ; uco-core:hasFacet [ a uco-observable:ContentDataFacet ; diff --git a/tests/case_utils/case_file/test_case_file.py b/tests/case_utils/case_file/test_case_file.py index acbad79..19ba199 100644 --- a/tests/case_utils/case_file/test_case_file.py +++ b/tests/case_utils/case_file/test_case_file.py @@ -18,6 +18,8 @@ import pytest import rdflib.plugins.sparql # type: ignore +import case_utils.ontology + _logger = logging.getLogger(os.path.basename(__file__)) IRI_UCO_CORE = "https://unifiedcyberontology.org/ontology/uco/core#" @@ -42,6 +44,8 @@ def load_graph( ) -> rdflib.Graph: in_graph = rdflib.Graph() in_graph.parse(filename) + # The queries in this test rely on the subclass hierarchy. Load it. + case_utils.ontology.load_subclass_hierarchy(in_graph) return in_graph @pytest.fixture diff --git a/tests/case_utils/case_sparql_construct/.gitignore b/tests/case_utils/case_sparql_construct/.gitignore index 6553b72..376ddf7 100644 --- a/tests/case_utils/case_sparql_construct/.gitignore +++ b/tests/case_utils/case_sparql_construct/.gitignore @@ -1,2 +1,2 @@ -output.json -output.ttl +subclass-*.ttl +w3-output.* diff --git a/tests/case_utils/case_sparql_construct/Makefile b/tests/case_utils/case_sparql_construct/Makefile index d0dabb3..d749fe7 100644 --- a/tests/case_utils/case_sparql_construct/Makefile +++ b/tests/case_utils/case_sparql_construct/Makefile @@ -18,11 +18,16 @@ top_srcdir := $(shell cd ../../.. ; pwd) tests_srcdir := $(top_srcdir)/tests all: \ - output.ttl + subclass-explicit-none.ttl \ + subclass-implicit-any.ttl \ + w3-output.json \ + w3-output.ttl check: \ - output.json \ - output.ttl + subclass-explicit-none.ttl \ + subclass-implicit-any.ttl \ + w3-output.json \ + w3-output.ttl source $(tests_srcdir)/venv/bin/activate \ && pytest \ --log-level=DEBUG @@ -34,16 +39,47 @@ clean: output.* \ _* -output.%: \ +subclass-explicit-none.ttl: \ $(tests_srcdir)/.venv.done.log \ $(top_srcdir)/case_utils/case_sparql_construct/__init__.py \ - input-1.sparql \ - input-2.ttl \ - input-3.json + $(top_srcdir)/case_utils/ontology/__init__.py \ + $(top_srcdir)/case_utils/ontology/version_info.py \ + subclass.json \ + subclass.sparql source $(tests_srcdir)/venv/bin/activate \ && case_sparql_construct \ + --built-version none \ _$@ \ - input-1.sparql \ - input-2.ttl \ - input-3.json + subclass.sparql \ + subclass.json + mv _$@ $@ + +subclass-implicit-any.ttl: \ + $(tests_srcdir)/.venv.done.log \ + $(top_srcdir)/case_utils/case_sparql_construct/__init__.py \ + $(top_srcdir)/case_utils/ontology/__init__.py \ + $(top_srcdir)/case_utils/ontology/version_info.py \ + subclass.json \ + subclass.sparql + source $(tests_srcdir)/venv/bin/activate \ + && case_sparql_construct \ + _$@ \ + subclass.sparql \ + subclass.json + mv _$@ $@ + +w3-output.%: \ + $(tests_srcdir)/.venv.done.log \ + $(top_srcdir)/case_utils/case_sparql_construct/__init__.py \ + $(top_srcdir)/case_utils/ontology/__init__.py \ + $(top_srcdir)/case_utils/ontology/version_info.py \ + w3-input-1.sparql \ + w3-input-2.ttl \ + w3-input-3.json + source $(tests_srcdir)/venv/bin/activate \ + && case_sparql_construct \ + _$@ \ + w3-input-1.sparql \ + w3-input-2.ttl \ + w3-input-3.json mv _$@ $@ diff --git a/tests/case_utils/case_sparql_construct/README.md b/tests/case_utils/case_sparql_construct/README.md index bab41ac..9c125f8 100644 --- a/tests/case_utils/case_sparql_construct/README.md +++ b/tests/case_utils/case_sparql_construct/README.md @@ -1,16 +1,30 @@ -# Test of CASE SPARQL CONSTRUCT query runner +# Tests of CASE SPARQL CONSTRUCT query runner +The tests in this directory confirms `case_sparql_construct` satisfies a base set of expected command line functionality. -## Test procedure -The tests in this directory confirms `case_sparql_construct` satisfies a base set of expected command line functionality. -1. Inputs - `input-2.ttl` and `input-3.json` contain a small graph split across two files, and `input-1.sparql` contains a SPARQL `CONSTRUCT` query. -2. Outputs - `output.ttl` is generated by using `case_sparql_construct` to run `input-1.sparql` against the two `input-*.*` graph files. This affirms that `case_sparql_construct` can read multiple input files of differing formats. -3. Output verification - two name-pairs should have vcard records generated. The test `test_templates_with_blank_nodes_result()` confirms those pairs are in the output graph. +## Test 1 - CONSTRUCT from W3C example + + +### Test procedure + +1. Inputs - `w3-input-2.ttl` and `w3-input-3.json` contain a small graph split across two files, and `w3-input-1.sparql` contains a SPARQL `CONSTRUCT` query. +2. Outputs - `w3-output.ttl` is generated by using `case_sparql_construct` to run `w3-input-1.sparql` against the two `w3-input-*.*` graph files. This affirms that `case_sparql_construct` can read multiple input files of differing formats. +3. Output verification - two name-pairs should have vcard records generated. The tests `test_w3_templates_with_blank_nodes_result_json()` and `_turtle()` confirm those pairs are in the output graphs. + + +### References + +The data and query used in `w3-input-2.ttl`, `w3-input-3.json` and `w3-input.sparql` are copied from ["SPARQL Query Language for RDF", Section 10.2.1](https://www.w3.org/TR/rdf-sparql-query/#tempatesWithBNodes), with these modifications: +* `w3-input-2.ttl` contains the original example's `_:a` (Alice) records, but drops the `_:b` (Bob) records. +* `w3-input-3.json` is a conversion of the original Turtle example's `_:b` records to JSON-LD. + + +## Test 2 - CASE subclass inference -## References +### Test procedure -The data and query used in `input-2.ttl`, `input-3.json` and `input.sparql` are copied from ["SPARQL Query Language for RDF", Section 10.2.1](https://www.w3.org/TR/rdf-sparql-query/#tempatesWithBNodes), with these modifications: -* `input-2.ttl` contains the original example's `_:a` (Alice) records, but drops the `_:b` (Bob) records. -* `input-3.json` is a conversion of the original Turtle example's `_:b` records to JSON-LD. +1. Inputs - `subclass.json` contain a small graph with two resources that are `ObservableObject`s. `kb:file-1` is explicitly an `ObservableObject`. `kb:file-2` is implicitly an `ObservableObject`, by way of `File` being a subclass of `ObservableObject`. `subclass.sparql` queries for all `ObservableObject`s, to construct an additional extending statement that they are also [`prov:Entity`](https://www.w3.org/TR/prov-o/#Entity)s. +2. Outputs - `subclass-implicit-any.ttl` is the constructed graph that uses CASE's subclass hierarchy. `subclass-explicit-none.ttl` is the constructed graph that foregoes any implicit CASE ontology structure, via the `--built-version none` flag. +3. Output verification - See the tests `test_subclass_templates_result_default_case` and `test_subclass_templates_result_no_case`, that verify foregoing the CASE subclass structure means one node is missed by the `CONSTRUCT` query. diff --git a/tests/case_utils/case_sparql_construct/subclass.json b/tests/case_utils/case_sparql_construct/subclass.json new file mode 100644 index 0000000..57646b1 --- /dev/null +++ b/tests/case_utils/case_sparql_construct/subclass.json @@ -0,0 +1,16 @@ +{ + "@context": { + "kb": "http://example.org/kb/", + "uco-observable": "https://unifiedcyberontology.org/ontology/uco/observable#" + }, + "@graph": [ + { + "@id": "kb:file-1", + "@type": "uco-observable:ObservableObject" + }, + { + "@id": "kb:file-2", + "@type": "uco-observable:File" + } + ] +} diff --git a/tests/case_utils/case_sparql_construct/subclass.sparql b/tests/case_utils/case_sparql_construct/subclass.sparql new file mode 100644 index 0000000..b0b1868 --- /dev/null +++ b/tests/case_utils/case_sparql_construct/subclass.sparql @@ -0,0 +1,9 @@ +PREFIX prov: +PREFIX uco-observable: +CONSTRUCT { + ?nEntity a prov:Entity . +} +WHERE +{ + ?nEntity a/rdfs:subClassOf* uco-observable:ObservableObject . +} diff --git a/tests/case_utils/case_sparql_construct/test_case_sparql_construct.py b/tests/case_utils/case_sparql_construct/test_case_sparql_construct.py index 1d919c1..b111d9e 100644 --- a/tests/case_utils/case_sparql_construct/test_case_sparql_construct.py +++ b/tests/case_utils/case_sparql_construct/test_case_sparql_construct.py @@ -17,7 +17,28 @@ import case_utils -def _test_templates_with_blank_nodes_result( +def _test_subclass_templates_result( + filename : str, + expected : typing.Set[str] +) -> None: + computed : typing.Set[str] = set() + + graph = rdflib.Graph() + graph.parse(filename) + + query_string = """\ +PREFIX prov: +SELECT ?nEntity +WHERE { + ?nEntity a prov:Entity +} +""" + for result in graph.query(query_string): + n_entity = result[0] + computed.add(n_entity.toPython()) + assert expected == computed + +def _test_w3_templates_with_blank_nodes_result( filename : str ) -> None: ground_truth_positive = { @@ -52,8 +73,25 @@ def _test_templates_with_blank_nodes_result( )) assert computed == ground_truth_positive -def test_templates_with_blank_nodes_result_json() -> None: - _test_templates_with_blank_nodes_result("output.json") +def test_w3_templates_with_blank_nodes_result_json() -> None: + _test_w3_templates_with_blank_nodes_result("w3-output.json") + +def test_w3_templates_with_blank_nodes_result_turtle() -> None: + _test_w3_templates_with_blank_nodes_result("w3-output.ttl") + +def test_subclass_templates_result_default_case() -> None: + _test_subclass_templates_result( + "subclass-implicit-any.ttl", + { + "http://example.org/kb/file-1", + "http://example.org/kb/file-2" + } + ) -def test_templates_with_blank_nodes_result_turtle() -> None: - _test_templates_with_blank_nodes_result("output.ttl") +def test_subclass_templates_result_no_case() -> None: + _test_subclass_templates_result( + "subclass-explicit-none.ttl", + { + "http://example.org/kb/file-1" + } + ) diff --git a/tests/case_utils/case_sparql_construct/input-1.sparql b/tests/case_utils/case_sparql_construct/w3-input-1.sparql similarity index 100% rename from tests/case_utils/case_sparql_construct/input-1.sparql rename to tests/case_utils/case_sparql_construct/w3-input-1.sparql diff --git a/tests/case_utils/case_sparql_construct/input-2.ttl b/tests/case_utils/case_sparql_construct/w3-input-2.ttl similarity index 100% rename from tests/case_utils/case_sparql_construct/input-2.ttl rename to tests/case_utils/case_sparql_construct/w3-input-2.ttl diff --git a/tests/case_utils/case_sparql_construct/input-3.json b/tests/case_utils/case_sparql_construct/w3-input-3.json similarity index 100% rename from tests/case_utils/case_sparql_construct/input-3.json rename to tests/case_utils/case_sparql_construct/w3-input-3.json diff --git a/tests/case_utils/case_sparql_select/.check-subclass-explicit-none.md b/tests/case_utils/case_sparql_select/.check-subclass-explicit-none.md new file mode 100644 index 0000000..5d9d1ef --- /dev/null +++ b/tests/case_utils/case_sparql_select/.check-subclass-explicit-none.md @@ -0,0 +1,3 @@ +| | ?nFile | +|----|------------------------------| +| 0 | http://example.org/kb/file-1 | \ No newline at end of file diff --git a/tests/case_utils/case_sparql_select/.check-subclass-implicit-any.md b/tests/case_utils/case_sparql_select/.check-subclass-implicit-any.md new file mode 100644 index 0000000..251fcf6 --- /dev/null +++ b/tests/case_utils/case_sparql_select/.check-subclass-implicit-any.md @@ -0,0 +1,4 @@ +| | ?nFile | +|----|------------------------------| +| 0 | http://example.org/kb/file-1 | +| 1 | http://example.org/kb/file-2 | \ No newline at end of file diff --git a/tests/case_utils/case_sparql_select/.check-output.html b/tests/case_utils/case_sparql_select/.check-w3-output.html similarity index 100% rename from tests/case_utils/case_sparql_select/.check-output.html rename to tests/case_utils/case_sparql_select/.check-w3-output.html diff --git a/tests/case_utils/case_sparql_select/.check-output.md b/tests/case_utils/case_sparql_select/.check-w3-output.md similarity index 100% rename from tests/case_utils/case_sparql_select/.check-output.md rename to tests/case_utils/case_sparql_select/.check-w3-output.md diff --git a/tests/case_utils/case_sparql_select/.gitignore b/tests/case_utils/case_sparql_select/.gitignore index a85ef3b..0ae4849 100644 --- a/tests/case_utils/case_sparql_select/.gitignore +++ b/tests/case_utils/case_sparql_select/.gitignore @@ -1,2 +1,2 @@ -output.html -output.md +subclass-*.md +w3-output.* diff --git a/tests/case_utils/case_sparql_select/Makefile b/tests/case_utils/case_sparql_select/Makefile index 6070739..22a849b 100644 --- a/tests/case_utils/case_sparql_select/Makefile +++ b/tests/case_utils/case_sparql_select/Makefile @@ -18,28 +18,49 @@ top_srcdir := $(shell cd ../../.. ; pwd) tests_srcdir := $(top_srcdir)/tests all: \ - output.html \ - output.md + subclass-explicit-none.md \ + subclass-implicit-any.md \ + w3-output.html \ + w3-output.md .PHONY: \ - check-html \ - check-markdown + check-subclass \ + check-subclass-explicit-none \ + check-subclass-implicit-any \ + check-w3-html \ + check-w3-markdown .PRECIOUS: \ - output.% + subclass-% \ + w3-output.% check: \ - check-html \ - check-markdown + check-w3-html \ + check-w3-markdown \ + check-subclass -check-html: \ - .check-output.html \ - output.html +check-subclass: \ + check-subclass-explicit-none \ + check-subclass-implicit-any + +check-subclass-explicit-none: \ + .check-subclass-explicit-none.md \ + subclass-explicit-none.md + diff $^ + +check-subclass-implicit-any: \ + .check-subclass-implicit-any.md \ + subclass-implicit-any.md + diff $^ + +check-w3-html: \ + .check-w3-output.html \ + w3-output.html diff $^ -check-markdown: \ - .check-output.md \ - output.md +check-w3-markdown: \ + .check-w3-output.md \ + w3-output.md diff $^ clean: @@ -50,16 +71,47 @@ clean: *.md \ _* -output.%: \ +subclass-explicit-none.md: \ + $(tests_srcdir)/.venv.done.log \ + $(top_srcdir)/case_utils/case_sparql_select/__init__.py \ + $(top_srcdir)/case_utils/ontology/__init__.py \ + $(top_srcdir)/case_utils/ontology/version_info.py \ + subclass.json \ + subclass.sparql + source $(tests_srcdir)/venv/bin/activate \ + && case_sparql_select \ + --built-version none \ + _$@ \ + subclass.sparql \ + subclass.json + mv _$@ $@ + +subclass-implicit-any.md: \ + $(tests_srcdir)/.venv.done.log \ + $(top_srcdir)/case_utils/case_sparql_select/__init__.py \ + $(top_srcdir)/case_utils/ontology/__init__.py \ + $(top_srcdir)/case_utils/ontology/version_info.py \ + subclass.json \ + subclass.sparql + source $(tests_srcdir)/venv/bin/activate \ + && case_sparql_select \ + _$@ \ + subclass.sparql \ + subclass.json + mv _$@ $@ + +w3-output.%: \ $(tests_srcdir)/.venv.done.log \ $(top_srcdir)/case_utils/case_sparql_select/__init__.py \ - input-1.sparql \ - input-2.ttl \ - input-3.json + $(top_srcdir)/case_utils/ontology/__init__.py \ + $(top_srcdir)/case_utils/ontology/version_info.py \ + w3-input-1.sparql \ + w3-input-2.ttl \ + w3-input-3.json source $(tests_srcdir)/venv/bin/activate \ && case_sparql_select \ _$@ \ - input-1.sparql \ - input-2.ttl \ - input-3.json + w3-input-1.sparql \ + w3-input-2.ttl \ + w3-input-3.json mv _$@ $@ diff --git a/tests/case_utils/case_sparql_select/subclass.json b/tests/case_utils/case_sparql_select/subclass.json new file mode 100644 index 0000000..57646b1 --- /dev/null +++ b/tests/case_utils/case_sparql_select/subclass.json @@ -0,0 +1,16 @@ +{ + "@context": { + "kb": "http://example.org/kb/", + "uco-observable": "https://unifiedcyberontology.org/ontology/uco/observable#" + }, + "@graph": [ + { + "@id": "kb:file-1", + "@type": "uco-observable:ObservableObject" + }, + { + "@id": "kb:file-2", + "@type": "uco-observable:File" + } + ] +} diff --git a/tests/case_utils/case_sparql_select/subclass.sparql b/tests/case_utils/case_sparql_select/subclass.sparql new file mode 100644 index 0000000..9c52721 --- /dev/null +++ b/tests/case_utils/case_sparql_select/subclass.sparql @@ -0,0 +1,5 @@ +PREFIX uco-observable: +SELECT ?nFile +WHERE { + ?nFile a/rdfs:subClassOf* uco-observable:ObservableObject . +} diff --git a/tests/case_utils/case_sparql_select/input-1.sparql b/tests/case_utils/case_sparql_select/w3-input-1.sparql similarity index 100% rename from tests/case_utils/case_sparql_select/input-1.sparql rename to tests/case_utils/case_sparql_select/w3-input-1.sparql diff --git a/tests/case_utils/case_sparql_select/input-2.ttl b/tests/case_utils/case_sparql_select/w3-input-2.ttl similarity index 100% rename from tests/case_utils/case_sparql_select/input-2.ttl rename to tests/case_utils/case_sparql_select/w3-input-2.ttl diff --git a/tests/case_utils/case_sparql_select/input-3.json b/tests/case_utils/case_sparql_select/w3-input-3.json similarity index 100% rename from tests/case_utils/case_sparql_select/input-3.json rename to tests/case_utils/case_sparql_select/w3-input-3.json