From a9a5678be6a572b9a33f08e1d4189e8e45a33017 Mon Sep 17 00:00:00 2001 From: Kate Studzinski Date: Tue, 10 Sep 2024 15:12:52 -0400 Subject: [PATCH 01/10] Issue #116 Option to show bnodes in 'graphic' mode --- README.md | 2 + onto_tool/command_line.py | 3 ++ onto_tool/onto_tool.py | 5 +- onto_tool/ontograph.py | 38 +++++++++----- tests/graphic/issue-116-bnode-subjects.ttl | 14 ++++++ tests/graphic/issue-116-named-subjects.ttl | 14 ++++++ tests/graphic/issue-116-ontology.ttl | 16 ++++++ tests/graphic/test_bnode_subject.py | 58 ++++++++++++++++++++++ 8 files changed, 136 insertions(+), 14 deletions(-) create mode 100644 tests/graphic/issue-116-bnode-subjects.ttl create mode 100644 tests/graphic/issue-116-named-subjects.ttl create mode 100644 tests/graphic/issue-116-ontology.ttl create mode 100644 tests/graphic/test_bnode_subject.py diff --git a/README.md b/README.md index 5c6aece..6ab5f63 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,8 @@ optional arguments: Title to use for graph. If not supplied, the repo URI will be used if graphing an endpoint, or 'Gist' if graphing local files. + --show-bnode-subjects Use triples with blank nodes in the subject to generate + the graphic. Sampling Limits: --instance-limit INSTANCE_LIMIT diff --git a/onto_tool/command_line.py b/onto_tool/command_line.py index e961494..a48aec8 100644 --- a/onto_tool/command_line.py +++ b/onto_tool/command_line.py @@ -198,6 +198,9 @@ def define_graphic_parser(subparsers): " graphing an endpoint, or 'Gist' if graphing local files.") graphic_parser.add_argument('ontology', nargs="*", default=[], help="Ontology file, directory or name pattern") + graphic_parser.add_argument("--show-bnode-subjects", action="store_true", + help="Use triples with blank nodes in the subject to generate " + "the graphic.") def define_export_parser(subparsers): diff --git a/onto_tool/onto_tool.py b/onto_tool/onto_tool.py index bfee329..069a51f 100644 --- a/onto_tool/onto_tool.py +++ b/onto_tool/onto_tool.py @@ -169,6 +169,8 @@ def generate_graphic(action, onto_files, endpoint, **kwargs): Read cached query results save_cache: TextIOWrapper Save query results as JSON to use with --cache + show_bnode_subjects: boolean + If true, triples with blank nodes in the subject will be used to generate the graphic Returns ------- @@ -308,7 +310,8 @@ def main(arguments): exclude_pattern=args.exclude_pattern, show_shacl=args.show_shacl, cache=args.cache, - save_cache=args.save_cache) + save_cache=args.save_cache, + show_bnode_subjects=args.show_bnode_subjects) return of = 'pretty-xml' if args.format == 'xml' else args.format diff --git a/onto_tool/ontograph.py b/onto_tool/ontograph.py index b7bc896..b4cdb49 100644 --- a/onto_tool/ontograph.py +++ b/onto_tool/ontograph.py @@ -84,6 +84,7 @@ def __init__(self, files, repo=None, **kwargs): self.show_shacl = kwargs.get('show_shacl') self.shapes = defaultdict(list) + self.show_bnode_subjects = kwargs.get('show_bnode_subjects') def __configure_data_source(self, repo, kwargs, title, version): """Determine graph title and output location from data source.""" @@ -172,7 +173,7 @@ def gather_schema_info_from_files(self): ontology = next(graph.subjects(RDF.type, OWL.Ontology)) ontology_name = self.__strip_uri(ontology) classes = [self.__strip_uri(c) for c in graph.subjects(RDF.type, OWL.Class) - if not isinstance(c, BNode)] + if not isinstance(c, BNode) or self.show_bnode_subjects] obj_props = [self.__strip_uri(c) for c in graph.subjects(RDF.type, OWL.ObjectProperty)] data_props = [self.__strip_uri(c) @@ -182,8 +183,8 @@ def gather_schema_info_from_files(self): all_seen = set(classes + obj_props + data_props + annotation_props) gist_things = [ self.__strip_uri(s) for (s, o) in graph.subject_objects(RDF.type) - if not isinstance(s, BNode) and not s == ontology and not self.__strip_uri(s) - in all_seen] + if (not isinstance(s, BNode) or self.show_bnode_subjects) and + not s == ontology and not self.__strip_uri(s) in all_seen] imports = [self.__strip_uri(c) for c in graph.objects(ontology, OWL.imports)] @@ -202,6 +203,7 @@ def gather_schema_info_from_files(self): def gather_schema_info_from_repo(self): """Load schema data from SPARQL endpoint.""" onto_data = defaultdict(lambda: defaultdict(list)) + bnode_filter = "filter(!ISBLANK(?entity))" if not self.show_bnode_subjects else "" if self.single_graph: onto_query = """ prefix owl: @@ -221,7 +223,7 @@ def gather_schema_info_from_repo(self): { ?entity a ?type . FILTER(?type != owl:Ontology) - filter(!ISBLANK(?entity)) + $bnode_filter } } } @@ -243,7 +245,7 @@ def gather_schema_info_from_repo(self): UNION { ?entity rdfs:isDefinedBy ?ontology; a ?type . - filter(!ISBLANK(?entity)) + ?bnode_filter } } """ @@ -254,7 +256,8 @@ def gather_schema_info_from_repo(self): str(OWL.AnnotationProperty): 'annotation_propertiesList', str(OWL.imports): 'imports' } - for entity in self.__remote_select_query(onto_query): + for entity in self.__remote_select_query( + Template(onto_query).substitute(bnode_filter=bnode_filter)): key = mapping.get(entity['type'], 'gist_thingsList') onto_data[entity['ontology']][key].append( self.__strip_uri(entity['entity'])) @@ -481,6 +484,8 @@ def gather_instance_info(self): json.dump(self.cached_data, self.save_cache) def __build_class_hierarchy(self): + bnode_filter = "filter (!isblank(?class) && !isblank(?parent))\n" \ + if not self.show_bnode_subjects else "" inheritance_query = Template(""" prefix owl: prefix rdf: @@ -498,7 +503,7 @@ def __build_class_hierarchy(self): rdf:rest*/rdf:first ?parent . ?parent a owl:Class } - filter (!isblank(?class) && !isblank(?parent)) + $bnode_filter OPTIONAL { ?class rdfs:label|skos:prefLabel ?c_label FILTER(lang(?c_label) = '$language' || lang(?c_label) = '') @@ -508,7 +513,7 @@ def __build_class_hierarchy(self): FILTER(lang(?p_label) = '$language' || lang(?p_label) = '') } } - """).substitute(language=self.label_lang) + """).substitute(bnode_filter=bnode_filter, language=self.label_lang) parents = self.__select_query(inheritance_query, 'parents') for inheritance_info in parents: @@ -634,6 +639,7 @@ def __add_shacl_coloring(self): self.shapes[row['class']].append(row['property']) def __create_class_count_query(self, limit): + bnode_filter = "FILTER(!ISBLANK(?s))" if not self.show_bnode_subjects else "" class_query = """ prefix owl: prefix rdf: @@ -646,7 +652,7 @@ def __create_class_count_query(self, limit): { select (group_concat(?o;separator=' ') as ?src) where { $pattern - FILTER(!ISBLANK(?s)) + $bnode_filter FILTER (!STRSTARTS(STR(?o), 'http://www.w3.org/2002/07/owl#')) } group by ?s LIMIT $limit } @@ -654,10 +660,14 @@ def __create_class_count_query(self, limit): """ query_text = Template(class_query).substitute( pattern=self.__filtered_graph_pattern(str(RDF.type)), + bnode_filter=bnode_filter, limit=limit) return query_text def __create_predicate_query(self, predicate, predicate_type, limit): + bnode_filter = "FILTER(!ISBLANK(?s))" if not self.show_bnode_subjects else "" + bnode_and_literal_filter = "FILTER(!ISBLANK(?s) && ISLITERAL(?o))" if not self.show_bnode_subjects \ + else "FILTER(ISLITERAL(?o))" if predicate_type == str(OWL.ObjectProperty): type_query = """ prefix owl: @@ -674,7 +684,7 @@ def __create_predicate_query(self, predicate, predicate_type, limit): (group_concat(?tgt_c;separator=' ') as ?tgt) where { $pattern - FILTER(!ISBLANK(?s)) + $bnode_filter ?s a ?src_c . FILTER (!STRSTARTS(STR(?src_c), 'http://www.w3.org/2002/07/owl#')) ?o a ?tgt_c . @@ -695,7 +705,7 @@ def __create_predicate_query(self, predicate, predicate_type, limit): { select (group_concat(?src_c;separator=' ') as ?src) (SAMPLE(COALESCE(?dtype, xsd:string)) as ?dt) where { $pattern - FILTER(!ISBLANK(?s) && ISLITERAL(?o)) + $bnode_and_literal_filter ?s a ?src_c . FILTER (!STRSTARTS(STR(?src_c), 'http://www.w3.org/2002/07/owl#')) BIND(DATATYPE(?o) as ?dtype) . @@ -724,7 +734,7 @@ def __create_predicate_query(self, predicate, predicate_type, limit): select (group_concat(?src_c;separator=' ') as ?src) (group_concat(?tgt_c;separator=' ') as ?tgt) where { $pattern - FILTER(!ISBLANK(?s)) + $bnode_filter ?s a ?src_c . FILTER (!STRSTARTS(STR(?src_c), 'http://www.w3.org/2002/07/owl#')) FILTER (!STRSTARTS(STR(?src_c), 'http://www.w3.org/ns/shacl#')) @@ -742,7 +752,7 @@ def __create_predicate_query(self, predicate, predicate_type, limit): select (group_concat(?src_c;separator=' ') as ?src) (SAMPLE(COALESCE(?dtype, xsd:string)) as ?dt) where { $pattern - FILTER(!ISBLANK(?s) && ISLITERAL(?o)) + $bnode_and_literal_filter ?s a ?src_c . FILTER (!STRSTARTS(STR(?src_c), 'http://www.w3.org/2002/07/owl#')) FILTER (!STRSTARTS(STR(?src_c), 'http://www.w3.org/ns/shacl#')) @@ -756,6 +766,8 @@ def __create_predicate_query(self, predicate, predicate_type, limit): """ query_text = Template(type_query).substitute( pattern=self.__filtered_graph_pattern(predicate), + bnode_filter=bnode_filter, + bnode_and_literal_filter=bnode_and_literal_filter, limit=limit) return query_text diff --git a/tests/graphic/issue-116-bnode-subjects.ttl b/tests/graphic/issue-116-bnode-subjects.ttl new file mode 100644 index 0000000..c3bfc09 --- /dev/null +++ b/tests/graphic/issue-116-bnode-subjects.ttl @@ -0,0 +1,14 @@ +@prefix : . +@prefix rdf: . + +[] a :Person ; + :name "James" ; + :residesIn :_Virginia ; + . +[] a :Person ; + :name "Betty" ; + :residesIn :_Maryland ; + . + +:_Virginia a :State . +:_Maryland a :State . diff --git a/tests/graphic/issue-116-named-subjects.ttl b/tests/graphic/issue-116-named-subjects.ttl new file mode 100644 index 0000000..b5f18f0 --- /dev/null +++ b/tests/graphic/issue-116-named-subjects.ttl @@ -0,0 +1,14 @@ +@prefix : . +@prefix rdf: . + +:_James a :Person ; + :name "James" ; + :residesIn :_Virginia ; + . +:_Betty a :Person ; + :name "Betty" ; + :residesIn :_Maryland ; + . + +:_Virginia a :State . +:_Maryland a :State . diff --git a/tests/graphic/issue-116-ontology.ttl b/tests/graphic/issue-116-ontology.ttl new file mode 100644 index 0000000..77aeac4 --- /dev/null +++ b/tests/graphic/issue-116-ontology.ttl @@ -0,0 +1,16 @@ +@prefix : . +@prefix owl: . +@prefix rdfs: . + +:SmallOnt a owl:Ontology . + +:Person a rdfs:Class . +:State a rdfs:Class . +:residesIn a owl:ObjectProperty . +:name a owl:DatatypeProperty . + +:Class1 a rdfs:Class . +:Class2 a rdfs:Class . +[] a rdfs:Class ; + rdfs:subClassOf :Class1, :Class2 ; + rdfs:label "Unnamed Class" . diff --git a/tests/graphic/test_bnode_subject.py b/tests/graphic/test_bnode_subject.py new file mode 100644 index 0000000..5383f6b --- /dev/null +++ b/tests/graphic/test_bnode_subject.py @@ -0,0 +1,58 @@ +import os +from onto_tool import onto_tool +import pydot +import re + + +def test_filter_bnode_subjects(): + onto_tool.main([ + 'graphic', '--predicate-threshold', '0', '--data', + '-t', 'Blank Node Subjects Filtered', + '--no-image', + '-o', 'tests-output/graphic/test_filter_bnode_subjects', + 'tests/graphic/issue-116-bnode-subjects.ttl' + ]) + assert not os.path.isfile('tests-output/graphic/test_filter_bnode_subjects.dot') + + +def test_show_bnode_subjects(): + onto_tool.main([ + 'graphic', '--predicate-threshold', '0', '--data', + '--show-bnode-subjects', + '-t', 'Blank Node Subjects Shown', + '--no-image', + '-o', 'tests-output/graphic/test_show_bnode_subjects', + 'tests/graphic/issue-116-bnode-subjects.ttl', + 'tests/graphic/issue-116-ontology.ttl' + ]) + (instance_graph,) = pydot.graph_from_dot_file('tests-output/graphic/test_show_bnode_subjects.dot') + edges = list(sorted((e.get_source(), e.get_destination()) for e in instance_graph.get_edges())) + assert edges == [('"http://example.org/Person"', '"http://example.org/State"')] + + +def test_show_named_subjects(): + onto_tool.main([ + 'graphic', '--predicate-threshold', '0', '--data', + '--show-bnode-subjects', + '-t', 'Named Node Subjects', + '--no-image', + '-o', 'tests-output/graphic/test_named_subjects', + 'tests/graphic/issue-116-named-subjects.ttl', + ]) + (instance_graph,) = pydot.graph_from_dot_file('tests-output/graphic/test_named_subjects.dot') + edges = list(sorted((e.get_source(), e.get_destination()) for e in instance_graph.get_edges())) + assert edges == [('"http://example.org/Person"', '"http://example.org/State"')] + + +def test_show_ont_bnode_subjects(): + onto_tool.main([ + 'graphic', '--predicate-threshold', '0', '--schema', + '--show-bnode-subjects', + '-t', 'Ontology Named Node Subjects', + '--no-image', + '-o', 'tests-output/graphic/test_ont_bnode_subjects', + 'tests/graphic/issue-116-ontology.ttl', + ]) + (instance_graph,) = pydot.graph_from_dot_file('tests-output/graphic/test_ont_bnode_subjects.dot') + label = instance_graph.get_nodes()[1].get("label") + assert re.sub(r"n[0-9a-fA-F]{34}", "BNODE_ID", label) == '"{issue-116-ontology.ttl\l\lSmallOnt||residesIn|name||Person\lState\lClass1\lClass2\lBNODE_ID}"' From 93daf38de74b6c5418c117e48ec63dabdcd05147 Mon Sep 17 00:00:00 2001 From: Kate Studzinski Date: Tue, 17 Sep 2024 14:52:43 -0400 Subject: [PATCH 02/10] Update documentation --- onto_tool/command_line.py | 3 +-- onto_tool/onto_tool.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/onto_tool/command_line.py b/onto_tool/command_line.py index a48aec8..966e346 100644 --- a/onto_tool/command_line.py +++ b/onto_tool/command_line.py @@ -199,8 +199,7 @@ def define_graphic_parser(subparsers): graphic_parser.add_argument('ontology', nargs="*", default=[], help="Ontology file, directory or name pattern") graphic_parser.add_argument("--show-bnode-subjects", action="store_true", - help="Use triples with blank nodes in the subject to generate " - "the graphic.") + help="Include blank node subjects when generating a graph.") def define_export_parser(subparsers): diff --git a/onto_tool/onto_tool.py b/onto_tool/onto_tool.py index 069a51f..1c0fccd 100644 --- a/onto_tool/onto_tool.py +++ b/onto_tool/onto_tool.py @@ -170,7 +170,7 @@ def generate_graphic(action, onto_files, endpoint, **kwargs): save_cache: TextIOWrapper Save query results as JSON to use with --cache show_bnode_subjects: boolean - If true, triples with blank nodes in the subject will be used to generate the graphic + If true, triples with blank nodes in the subject will not be used to filtered out. Returns ------- From 9b1db07efdd9b8f4db664056856585cc37e655d6 Mon Sep 17 00:00:00 2001 From: Kate Studzinski Date: Tue, 17 Sep 2024 14:57:36 -0400 Subject: [PATCH 03/10] Simplify bnode and literal filtering --- onto_tool/ontograph.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/onto_tool/ontograph.py b/onto_tool/ontograph.py index b4cdb49..54c6c17 100644 --- a/onto_tool/ontograph.py +++ b/onto_tool/ontograph.py @@ -666,8 +666,6 @@ def __create_class_count_query(self, limit): def __create_predicate_query(self, predicate, predicate_type, limit): bnode_filter = "FILTER(!ISBLANK(?s))" if not self.show_bnode_subjects else "" - bnode_and_literal_filter = "FILTER(!ISBLANK(?s) && ISLITERAL(?o))" if not self.show_bnode_subjects \ - else "FILTER(ISLITERAL(?o))" if predicate_type == str(OWL.ObjectProperty): type_query = """ prefix owl: @@ -705,7 +703,8 @@ def __create_predicate_query(self, predicate, predicate_type, limit): { select (group_concat(?src_c;separator=' ') as ?src) (SAMPLE(COALESCE(?dtype, xsd:string)) as ?dt) where { $pattern - $bnode_and_literal_filter + FILTER(ISLITERAL(?o)) + $bnode_filter ?s a ?src_c . FILTER (!STRSTARTS(STR(?src_c), 'http://www.w3.org/2002/07/owl#')) BIND(DATATYPE(?o) as ?dtype) . @@ -752,7 +751,8 @@ def __create_predicate_query(self, predicate, predicate_type, limit): select (group_concat(?src_c;separator=' ') as ?src) (SAMPLE(COALESCE(?dtype, xsd:string)) as ?dt) where { $pattern - $bnode_and_literal_filter + FILTER(ISLITERAL(?o)) + $bnode_filter ?s a ?src_c . FILTER (!STRSTARTS(STR(?src_c), 'http://www.w3.org/2002/07/owl#')) FILTER (!STRSTARTS(STR(?src_c), 'http://www.w3.org/ns/shacl#')) @@ -767,7 +767,6 @@ def __create_predicate_query(self, predicate, predicate_type, limit): query_text = Template(type_query).substitute( pattern=self.__filtered_graph_pattern(predicate), bnode_filter=bnode_filter, - bnode_and_literal_filter=bnode_and_literal_filter, limit=limit) return query_text From 7cbac385e321e39936c97d43fde9fe71af1b9dfa Mon Sep 17 00:00:00 2001 From: Kate Studzinski Date: Tue, 17 Sep 2024 15:01:12 -0400 Subject: [PATCH 04/10] Typo in Template variable --- onto_tool/ontograph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onto_tool/ontograph.py b/onto_tool/ontograph.py index 54c6c17..8bbb910 100644 --- a/onto_tool/ontograph.py +++ b/onto_tool/ontograph.py @@ -245,7 +245,7 @@ def gather_schema_info_from_repo(self): UNION { ?entity rdfs:isDefinedBy ?ontology; a ?type . - ?bnode_filter + $bnode_filter } } """ From f072089be5f712c9956cac7d9cc561f03fa43bb2 Mon Sep 17 00:00:00 2001 From: Kate Studzinski Date: Thu, 19 Sep 2024 12:30:48 -0400 Subject: [PATCH 05/10] Issue #133 Migrate test outputs to temp directory --- tests/bundle/__init__.py | 3 --- tests/bundle/test_markdown.py | 16 +++++------ tests/bundle/test_message.py | 4 +-- tests/bundle/test_sparql.py | 30 ++++++++++----------- tests/bundle/test_syntax_error.py | 4 +-- tests/bundle/test_transform.py | 16 +++++------ tests/bundle/test_verify.py | 38 +++++++++++++------------- tests/graphic/__init__.py | 4 +-- tests/graphic/test_bnode_subject.py | 24 ++++++++--------- tests/graphic/test_instance.py | 42 ++++++++++++++--------------- tests/graphic/test_schema.py | 7 ++--- 11 files changed, 92 insertions(+), 96 deletions(-) diff --git a/tests/bundle/__init__.py b/tests/bundle/__init__.py index a8dd25a..e69de29 100644 --- a/tests/bundle/__init__.py +++ b/tests/bundle/__init__.py @@ -1,3 +0,0 @@ -import os -if not os.path.isdir('tests-output/bundle'): - os.makedirs('tests-output/bundle') diff --git a/tests/bundle/test_markdown.py b/tests/bundle/test_markdown.py index eac761c..ff05bae 100644 --- a/tests/bundle/test_markdown.py +++ b/tests/bundle/test_markdown.py @@ -3,28 +3,28 @@ from os.path import basename -def test_markdown_table(): +def test_markdown_table(tmp_path): onto_tool.main([ - 'bundle', '-v', 'output', 'tests-output/bundle', + 'bundle', '-v', 'output', f'{tmp_path}', 'tests/bundle/markdown.yaml' ]) - html_text = open('tests-output/bundle/Table.html').read() + html_text = open(f'{tmp_path}/Table.html').read() assert ' Date: Thu, 19 Sep 2024 15:38:42 -0400 Subject: [PATCH 06/10] test_schema outdir pattern follows other tests --- tests/graphic/test_schema.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/graphic/test_schema.py b/tests/graphic/test_schema.py index efc1272..1213003 100644 --- a/tests/graphic/test_schema.py +++ b/tests/graphic/test_schema.py @@ -3,17 +3,16 @@ def test_local_instance(tmp_path): - out_file = tmp_path / 'test_schema' onto_tool.main([ 'graphic', '-t', 'Local Ontology', '--no-image', - '-o', f'{out_file}', + '-o', f'{tmp_path}/test_schema', 'tests/graphic/domain_ontology.ttl', 'tests/graphic/upper_ontology.ttl', 'tests/graphic/instance_data.ttl' ]) - (instance_graph,) = pydot.graph_from_dot_file(f'{out_file}.dot') + (instance_graph,) = pydot.graph_from_dot_file(f'{tmp_path}/test_schema.dot') edges = list(sorted((e.get_source(), e.get_destination()) for e in instance_graph.get_edges())) assert edges == [ ('Domain', 'Upper'), From 24e68b23cd220a35cd2499af3cd10ad39771657c Mon Sep 17 00:00:00 2001 From: Kate Studzinski Date: Fri, 20 Sep 2024 09:24:44 -0400 Subject: [PATCH 07/10] Remove extra newline --- tests/graphic/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/graphic/__init__.py b/tests/graphic/__init__.py index 8b13789..e69de29 100644 --- a/tests/graphic/__init__.py +++ b/tests/graphic/__init__.py @@ -1 +0,0 @@ - From 8f622199ed1e5db78358354cf0d6d1bff4533de5 Mon Sep 17 00:00:00 2001 From: Kate Studzinski Date: Fri, 20 Sep 2024 09:17:24 -0400 Subject: [PATCH 08/10] Issue #100 Add sparql-endpoint-fixture to test remote ontology --- tests/__init__.py | 3 ++ tests/graphic/domain_ontology.trig | 32 +++++++++++++++++++ tests/graphic/instance_data.trig | 16 ++++++++++ tests/graphic/issue-116-ontology.trig | 18 +++++++++++ tests/graphic/issue-116-ontology.ttl | 19 ++++++++---- tests/graphic/test_bnode_subject.py | 39 ++++++++++++++++++++++++ tests/graphic/test_instance.py | 23 ++++++++++++++ tests/graphic/test_schema.py | 44 +++++++++++++++++++++++++++ tests/graphic/upper_ontology.trig | 29 ++++++++++++++++++ 9 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 tests/graphic/domain_ontology.trig create mode 100644 tests/graphic/instance_data.trig create mode 100644 tests/graphic/issue-116-ontology.trig create mode 100644 tests/graphic/upper_ontology.trig diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..7477c34 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,3 @@ +pytest_plugins = [ + "sparql_endpoint_fixture.endpoint" +] diff --git a/tests/graphic/domain_ontology.trig b/tests/graphic/domain_ontology.trig new file mode 100644 index 0000000..102f54c --- /dev/null +++ b/tests/graphic/domain_ontology.trig @@ -0,0 +1,32 @@ +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix owl: . +@prefix : . + +:DomainOntGraph { + :Domain a owl:Ontology; owl:imports :Upper; rdfs:label "Domain Ontology" . + + :Student a owl:Class ; + rdfs:subClassOf :Person ; + rdfs:isDefinedBy :Domain ; + rdfs:label "Student" . + + :Teacher a owl:Class ; + rdfs:subClassOf :Person ; + rdfs:isDefinedBy :Domain ; + rdfs:label "Teacher" . + + :School a owl:Class ; + rdfs:isDefinedBy :Domain ; + rdfs:label "School" . + + :worksFor a owl:ObjectProperty ; + rdfs:isDefinedBy :Domain ; + rdfs:domain :Teacher ; + rdfs:range :School ; + rdfs:label "works for" . + + :teaches a owl:ObjectProperty ; + rdfs:label "teaches" . +} diff --git a/tests/graphic/instance_data.trig b/tests/graphic/instance_data.trig new file mode 100644 index 0000000..8fb45c6 --- /dev/null +++ b/tests/graphic/instance_data.trig @@ -0,0 +1,16 @@ +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix owl: . +@prefix sh: . +@prefix : . + +:InstanceDataGraph { + :Instances a owl:Ontology; owl:imports :Domain; rdfs:label "Instance Data" . + + :_t1 a :Teacher; :worksFor :_u1; :teaches :_s1; rdfs:isDefinedBy :Instances . + :_u1 a :School; rdfs:isDefinedBy :Instances . + :_s1 a :Student; :isFriendOf :_p1; rdfs:isDefinedBy :Instances . + :_p1 a :Person; :hasPhoneNumber "123-456-7890"; rdfs:isDefinedBy :Instances . + :_h1 a sh:NodeShape ; sh:targetClass :School ; rdfs:isDefinedBy :Instances . +} diff --git a/tests/graphic/issue-116-ontology.trig b/tests/graphic/issue-116-ontology.trig new file mode 100644 index 0000000..3f244b8 --- /dev/null +++ b/tests/graphic/issue-116-ontology.trig @@ -0,0 +1,18 @@ +@prefix : . +@prefix owl: . +@prefix rdfs: . + +:OntGraph { + :SmallOnt a owl:Ontology . + + :Person a rdfs:Class . + :State a rdfs:Class . + :residesIn a owl:ObjectProperty . + :name a owl:DatatypeProperty . + + :Class1 a rdfs:Class . + :Class2 a rdfs:Class . + [] a rdfs:Class ; + rdfs:subClassOf :Class1, :Class2 ; + rdfs:label "Unnamed Class" . +} diff --git a/tests/graphic/issue-116-ontology.ttl b/tests/graphic/issue-116-ontology.ttl index 77aeac4..53ac746 100644 --- a/tests/graphic/issue-116-ontology.ttl +++ b/tests/graphic/issue-116-ontology.ttl @@ -4,13 +4,20 @@ :SmallOnt a owl:Ontology . -:Person a rdfs:Class . -:State a rdfs:Class . -:residesIn a owl:ObjectProperty . -:name a owl:DatatypeProperty . +:Person a rdfs:Class ; + rdfs:isDefinedBy :SmallOnt . +:State a rdfs:Class ; + rdfs:isDefinedBy :SmallOnt . +:residesIn a owl:ObjectProperty ; + rdfs:isDefinedBy :SmallOnt . +:name a owl:DatatypeProperty ; + rdfs:isDefinedBy :SmallOnt . -:Class1 a rdfs:Class . -:Class2 a rdfs:Class . +:Class1 a rdfs:Class ; + rdfs:isDefinedBy :SmallOnt . +:Class2 a rdfs:Class ; + rdfs:isDefinedBy :SmallOnt . [] a rdfs:Class ; + rdfs:isDefinedBy :SmallOnt ; rdfs:subClassOf :Class1, :Class2 ; rdfs:label "Unnamed Class" . diff --git a/tests/graphic/test_bnode_subject.py b/tests/graphic/test_bnode_subject.py index 7cc4ac1..ee17e0f 100644 --- a/tests/graphic/test_bnode_subject.py +++ b/tests/graphic/test_bnode_subject.py @@ -56,3 +56,42 @@ def test_show_ont_bnode_subjects(tmp_path): (instance_graph,) = pydot.graph_from_dot_file(f'{tmp_path}/test_ont_bnode_subjects.dot') label = instance_graph.get_nodes()[1].get("label") assert re.sub(r"n[0-9a-fA-F]{34}", "BNODE_ID", label) == '"{issue-116-ontology.ttl\l\lSmallOnt||residesIn|name||Person\lState\lClass1\lClass2\lBNODE_ID}"' + + +def test_remote_show_ont_bnode_subjects(tmp_path, sparql_endpoint): + repo_uri = 'https://my.rdfdb.com/repo/sparql' + rdf_files = ['tests/graphic/issue-116-ontology.ttl'] + sparql_endpoint(repo_uri, rdf_files) + + print(tmp_path) + onto_tool.main([ + 'graphic', '--predicate-threshold', '0', '--schema', + '--endpoint', f'{repo_uri}', + '--show-bnode-subjects', + '-t', 'Ontology Named Node Subjects', + '--no-image', + '-o', f'{tmp_path}/test_ont_bnode_subjects' + ]) + (instance_graph,) = pydot.graph_from_dot_file(f'{tmp_path}/test_ont_bnode_subjects.dot') + label = instance_graph.get_nodes()[1].get("label") + assert re.sub(r"n[0-9a-fA-F]{34}", "BNODE_ID", label) == '"{http://example.org/SmallOnt\l\lSmallOnt||residesIn|name||Class1\lClass2\lPerson\lState\lBNODE_ID}"' + + +def test_remote_show_ont_trig(tmp_path, sparql_endpoint): + repo_uri = 'https://my.rdfdb.com/repo/sparql' + rdf_files = ['tests/graphic/issue-116-ontology.trig'] + sparql_endpoint(repo_uri, rdf_files) + + print(tmp_path) + onto_tool.main([ + 'graphic', '--predicate-threshold', '0', '--schema', + '--single-ontology-graphs', + '--endpoint', f'{repo_uri}', + '--show-bnode-subjects', + '-t', 'Ontology Named Node Subjects', + '--no-image', + '-o', f'{tmp_path}/test_ont_bnode_subjects' + ]) + (instance_graph,) = pydot.graph_from_dot_file(f'{tmp_path}/test_ont_bnode_subjects.dot') + label = instance_graph.get_nodes()[1].get("label") + assert re.sub(r"n[0-9a-fA-F]{34}", "BNODE_ID", label) == '"{http://example.org/SmallOnt\l\lSmallOnt||residesIn|name||Class1\lClass2\lPerson\lState\lBNODE_ID}"' diff --git a/tests/graphic/test_instance.py b/tests/graphic/test_instance.py index 1a1c497..f4c7551 100644 --- a/tests/graphic/test_instance.py +++ b/tests/graphic/test_instance.py @@ -22,6 +22,29 @@ def test_local_instance(tmp_path): ] +def test_remote_instance(tmp_path, sparql_endpoint): + repo_uri = 'https://my.rdfdb.com/repo/sparql' + rdf_files = ['tests/graphic/domain_ontology.ttl', + 'tests/graphic/upper_ontology.ttl', + 'tests/graphic/instance_data.ttl'] + sparql_endpoint(repo_uri, rdf_files) + + onto_tool.main([ + 'graphic', '--predicate-threshold', '0', '--data', + '--endpoint', f'{repo_uri}', + '-t', 'Remote Instance Data', + '--no-image', + '-o', f'{tmp_path}/test_instance' + ]) + (instance_graph,) = pydot.graph_from_dot_file(f'{tmp_path}/test_instance.dot') + edges = list(sorted((e.get_source(), e.get_destination()) for e in instance_graph.get_edges())) + assert edges == [ + ('"http://example.com/Student"', '"http://example.com/Person"'), + ('"http://example.com/Teacher"', '"http://example.com/School"'), + ('"http://example.com/Teacher"', '"http://example.com/Student"') + ] + + def test_multi_language(tmp_path): onto_tool.main([ 'graphic', '--predicate-threshold', '0', '--data', diff --git a/tests/graphic/test_schema.py b/tests/graphic/test_schema.py index 1213003..068b4f3 100644 --- a/tests/graphic/test_schema.py +++ b/tests/graphic/test_schema.py @@ -18,3 +18,47 @@ def test_local_instance(tmp_path): ('Domain', 'Upper'), ('Instances', 'Domain') ] + + +def test_remote_instance(tmp_path, sparql_endpoint): + repo_uri = 'https://my.rdfdb.com/repo/sparql' + rdf_files = ['tests/graphic/domain_ontology.ttl', + 'tests/graphic/upper_ontology.ttl', + 'tests/graphic/instance_data.ttl'] + sparql_endpoint(repo_uri, rdf_files) + + onto_tool.main([ + 'graphic', + '--endpoint', f'{repo_uri}', + '-t', 'Remote Ontology', + '--no-image', + '-o', f'{tmp_path}/test_schema' + ]) + (instance_graph,) = pydot.graph_from_dot_file(f'{tmp_path}/test_schema.dot') + edges = list(sorted((e.get_source(), e.get_destination()) for e in instance_graph.get_edges())) + assert edges == [ + ('Domain', 'Upper'), + ('Instances', 'Domain') + ] + + +def test_remote_graphs(tmp_path, sparql_endpoint): + repo_uri = 'https://my.rdfdb.com/repo/sparql' + rdf_files = ['tests/graphic/domain_ontology.trig', + 'tests/graphic/upper_ontology.trig', + 'tests/graphic/instance_data.trig'] + sparql_endpoint(repo_uri, rdf_files) + + onto_tool.main([ + 'graphic', '--single-ontology-graphs', + '--endpoint', f'{repo_uri}', + '-t', 'Remote Ontology', + '--no-image', + '-o', f'{tmp_path}/test_schema' + ]) + (instance_graph,) = pydot.graph_from_dot_file(f'{tmp_path}/test_schema.dot') + edges = list(sorted((e.get_source(), e.get_destination()) for e in instance_graph.get_edges())) + assert edges == [ + ('Domain', 'Upper'), + ('Instances', 'Domain') + ] diff --git a/tests/graphic/upper_ontology.trig b/tests/graphic/upper_ontology.trig new file mode 100644 index 0000000..f9d4d96 --- /dev/null +++ b/tests/graphic/upper_ontology.trig @@ -0,0 +1,29 @@ +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix owl: . +@prefix : . + +:UpperOntGraph { + :Upper a owl:Ontology; rdfs:label "Upper Ontology" . + + :Person a owl:Class ; + rdfs:isDefinedBy :Upper ; + rdfs:label "Person" . + + :isPrivate a owl:AnnotationProperty ; + rdfs:isDefinedBy :Upper ; + rdfs:range xsd:boolean ; + rdfs:label "is private" . + + :hasPhoneNumber a owl:DatatypeProperty ; + rdfs:isDefinedBy :Upper ; + rdfs:range xsd:string ; + rdfs:label "has phone number" . + + :isFriendOf a owl:ObjectProperty ; + rdfs:isDefinedBy :Upper ; + rdfs:domain :Person ; + rdfs:range :Person ; + rdfs:label "is friend of" . +} From b8fcae138a13a9c55452cb9e4b72ccf2e8fcc293 Mon Sep 17 00:00:00 2001 From: Kate Studzinski Date: Fri, 20 Sep 2024 13:37:12 -0400 Subject: [PATCH 09/10] Add sparql endpoin fixture to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 26570dd..f19d736 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,3 +18,4 @@ namedentities>=1.5.2 zipp>=3.1.0 pytest~=6.1.2 SPARQLWrapper~=1.8.5 +sparql-endpoint-fixture>=0.5.0 From 312ab8ca4974324ca1c4421fd7fce3461b771562 Mon Sep 17 00:00:00 2001 From: Kate Studzinski Date: Wed, 23 Oct 2024 18:18:42 -0400 Subject: [PATCH 10/10] Update setuptools version requirement --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f19d736..af9b8e9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ isodate>=0.6.0 jsonschema>=3.2.0 attrs>=19.3.0 pyrsistent>=0.16.0 -setuptools>=40.8.0 +setuptools<58.0.0 namedentities>=1.5.2 zipp>=3.1.0 pytest~=6.1.2