From d1385ea93a6591d75fd6d0d523b7660271a3ce3d Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Wed, 24 Jun 2020 17:54:29 +0200 Subject: [PATCH 01/11] [tools/RDFWriter] Make RDF subclassing optional Closes #394 --- odml/tools/rdf_converter.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/odml/tools/rdf_converter.py b/odml/tools/rdf_converter.py index 205ec7c6..56a0305e 100644 --- a/odml/tools/rdf_converter.py +++ b/odml/tools/rdf_converter.py @@ -57,14 +57,19 @@ class RDFWriter(object): """ A writer to parse odML files into RDF documents. + Use the 'rdf_subclassing' flag to disable default usage + of Section type conversion to RDF Subclasses. + Usage: RDFWriter(odml_docs).get_rdf_str('turtle') RDFWriter(odml_docs).write_file("/output_path", "rdf_format") """ - def __init__(self, odml_documents): + def __init__(self, odml_documents, rdf_subclassing=True): """ :param odml_documents: list of odML documents + :param rdf_subclassing: Flag whether Section types should be converted to RDF Subclasses + for enhanced SPARQL queries. Default is 'True'. """ if not isinstance(odml_documents, list): odml_documents = [odml_documents] @@ -75,6 +80,7 @@ def __init__(self, odml_documents): self.graph.bind("odml", ODML_NS) self.section_subclasses = load_rdf_subclasses() + self.rdf_subclassing = rdf_subclassing def convert_to_rdf(self): """ @@ -221,16 +227,26 @@ def save_section(self, sec, curr_node): # Add type of current node to the RDF graph curr_type = fmt.rdf_type + + print(curr_type) + # Handle section subclass types - sub_sec = self._get_section_subclass(sec) - if sub_sec: - curr_type = sub_sec + if self.rdf_subclassing: + print("I'm in here") + sub_sec = self._get_section_subclass(sec) + if sub_sec: + curr_type = sub_sec + + print(curr_type) + self.graph.add((curr_node, RDF.type, URIRef(curr_type))) for k in fmt.rdf_map_keys: curr_pred = fmt.rdf_map(k) curr_val = getattr(sec, k) + print("pred: %s; val: %s" % (curr_pred, curr_val)) + # Ignore an "id" entry, it has already been used to create the node itself. if k == "id" or not curr_val: continue From 030888d19c482ceffd2d7a2d5f9c2a69abae91f6 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Wed, 8 Jul 2020 15:57:01 +0200 Subject: [PATCH 02/11] [test/RDFWriter] Test RDF subclassing optional --- test/test_rdf_writer.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/test_rdf_writer.py b/test/test_rdf_writer.py index e3a4a23a..7927f98b 100644 --- a/test/test_rdf_writer.py +++ b/test/test_rdf_writer.py @@ -288,3 +288,25 @@ def test_get_rdf_string(self): with self.assertRaises(ValueError): rdf_writer.get_rdf_str("abc") + + def test_rdf_subclassing_switch(self): + """ + Test the RDF section subclassing switch. + """ + # Section type term defined in odml/resources/section_subclasses.yaml that will + # be converted to an RDF Section Subclass of Class "Cell". + sub_class_term = "cell" + + # Create minimal document + doc = odml.Document() + _ = odml.Section(name="test_subclassing", type=sub_class_term, parent=doc) + + # Test default subclassing + rdf_writer = RDFWriter([doc]) + result = rdf_writer.get_rdf_str() + self.assertIn("odml:Cell", result) + + # Test inactivation of subclassing feature + rdf_writer = RDFWriter([doc], rdf_subclassing=False) + result = rdf_writer.get_rdf_str() + self.assertNotIn("odml:Cell", result) From a4f13766723152442534ec5d7bed4ce616872328 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 9 Jul 2020 17:33:30 +0200 Subject: [PATCH 03/11] [tools/RDFWriter] Add custom RDF Subclassing Closes #395 --- odml/tools/rdf_converter.py | 60 ++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/odml/tools/rdf_converter.py b/odml/tools/rdf_converter.py index 56a0305e..a34c03af 100644 --- a/odml/tools/rdf_converter.py +++ b/odml/tools/rdf_converter.py @@ -5,6 +5,7 @@ import os import uuid +import warnings from io import StringIO from rdflib import Graph, Literal, URIRef @@ -57,19 +58,32 @@ class RDFWriter(object): """ A writer to parse odML files into RDF documents. - Use the 'rdf_subclassing' flag to disable default usage - of Section type conversion to RDF Subclasses. + Use the 'rdf_subclassing' flag to disable default usage of Section type conversion to + RDF Subclasses. + Provide a custom Section type to RDF Subclass Name mapping dictionary via the + 'custom_subclasses' attribute to add custom or overwrite default RDF Subclass mappings. Usage: RDFWriter(odml_docs).get_rdf_str('turtle') RDFWriter(odml_docs).write_file("/output_path", "rdf_format") + + RDFWriter(odml_docs, rdf_subclassing=False).write_file("path", "rdf_format") + RDFWriter(odml_docs, custom_subclasses=custom_dict).write_file("path", "rdf_format") """ - def __init__(self, odml_documents, rdf_subclassing=True): + def __init__(self, odml_documents, rdf_subclassing=True, custom_subclasses=None): """ :param odml_documents: list of odML documents :param rdf_subclassing: Flag whether Section types should be converted to RDF Subclasses for enhanced SPARQL queries. Default is 'True'. + :param custom_subclasses: A dict where the keys reference a Section type and the + corresponding values reference an RDF Class Name. When exporting + a Section of a type contained in this dict, the resulting RDF + Instance will be of the corresponding Class and this Class will + be added as a Subclass of RDF Class "odml:Section" to the + RDF document. + Key:value pairs of the "custom_subclasses" dict will overwrite + existing key:value pairs of the default subclassing dict. """ if not isinstance(odml_documents, list): odml_documents = [odml_documents] @@ -79,9 +93,14 @@ def __init__(self, odml_documents, rdf_subclassing=True): self.graph = Graph() self.graph.bind("odml", ODML_NS) - self.section_subclasses = load_rdf_subclasses() self.rdf_subclassing = rdf_subclassing + self.section_subclasses = load_rdf_subclasses() + # If a custom Section type to RDF Subclass dict has been provided, + # parse it and update the default section_subclasses dict with the content. + if custom_subclasses and isinstance(custom_subclasses, dict): + self._parse_custom_subclasses(custom_subclasses) + def convert_to_rdf(self): """ convert_to_rdf converts all odML documents to RDF, @@ -228,25 +247,18 @@ def save_section(self, sec, curr_node): # Add type of current node to the RDF graph curr_type = fmt.rdf_type - print(curr_type) - # Handle section subclass types if self.rdf_subclassing: - print("I'm in here") sub_sec = self._get_section_subclass(sec) if sub_sec: curr_type = sub_sec - print(curr_type) - self.graph.add((curr_node, RDF.type, URIRef(curr_type))) for k in fmt.rdf_map_keys: curr_pred = fmt.rdf_map(k) curr_val = getattr(sec, k) - print("pred: %s; val: %s" % (curr_pred, curr_val)) - # Ignore an "id" entry, it has already been used to create the node itself. if k == "id" or not curr_val: continue @@ -310,6 +322,32 @@ class Section. return None + def _parse_custom_subclasses(self, custom_subclasses): + """ + Parses a provided dictionary of "Section type": "RDF Subclass name" + key value pairs and adds the pairs to the parsers' 'section_subclasses' + default dictionary. Existing key:value pairs will be overwritten + with provided custom key:value pairs and a Warning will be issued. + Dictionary values containing whitespaces will raise a ValueError. + + :param custom_subclasses: dictionary of "Section type": "RDF Subclass name" key value pairs. + Values must not contain whitespaces, a ValueError will be raised + otherwise. + """ + + # Do not allow whitespaces in values + if " " in "".join(custom_subclasses.values()): + msg = "Custom RDF Subclass names must not contain any whitespaces." + raise ValueError(msg) + + for k in custom_subclasses: + val = custom_subclasses[k] + if k in self.section_subclasses: + msg = "RDFWriter custom subclasses: Key '%s' already exists. " % k + msg += "Value '%s' replaces default value '%s'." % (val, self.section_subclasses[k]) + warnings.warn(msg, stacklevel=2) + self.section_subclasses[k] = val + def __str__(self): return self.convert_to_rdf().serialize(format='turtle').decode("utf-8") From d15a09794a8c10f5b440c5f510eae9816994464f Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 9 Jul 2020 17:34:21 +0200 Subject: [PATCH 04/11] [test/RDFWriter] Test custom RDF subclassing --- test/test_rdf_writer.py | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/test_rdf_writer.py b/test/test_rdf_writer.py index 7927f98b..de40356d 100644 --- a/test/test_rdf_writer.py +++ b/test/test_rdf_writer.py @@ -310,3 +310,47 @@ def test_rdf_subclassing_switch(self): rdf_writer = RDFWriter([doc], rdf_subclassing=False) result = rdf_writer.get_rdf_str() self.assertNotIn("odml:Cell", result) + + def test_rdf_custom_subclasses(self): + sub_class_term = "cell" + + # Create minimal document + doc = odml.Document() + _ = odml.Section(name="test_subclassing", type=sub_class_term, parent=doc) + + # Test None dict + rdf_writer = RDFWriter([doc], custom_subclasses=None) + self.assertIn("odml:Cell", rdf_writer.get_rdf_str()) + + # Test invalid dict + rdf_writer = RDFWriter([doc], custom_subclasses=["invalid"]) + self.assertIn("odml:Cell", rdf_writer.get_rdf_str()) + + # Test value whitespace + invalid_dict = {"type_1": "Class 1", "type_2": "Class 2"} + with self.assertRaises(ValueError): + _ = RDFWriter([doc], custom_subclasses=invalid_dict) + + # Test custom subclassing + type_custom_class = "species" + custom_class_dict = {type_custom_class: "Species"} + + doc = odml.Document() + _ = odml.Section(name="test_subclassing", type="cell", parent=doc) + _ = odml.Section(name="test_custom_subclassing", type=type_custom_class, parent=doc) + + rdf_writer = RDFWriter([doc], custom_subclasses=custom_class_dict) + self.assertIn("odml:Cell", rdf_writer.get_rdf_str()) + self.assertIn("odml:Species", rdf_writer.get_rdf_str()) + + # Test custom subclassing overwrite + sub_class_type = "cell" + custom_class_dict = {sub_class_type: "Neuron"} + + doc = odml.Document() + _ = odml.Section(name="test_subclassing", type=sub_class_type, parent=doc) + + with self.assertWarns(UserWarning): + rdf_writer = RDFWriter([doc], custom_subclasses=custom_class_dict) + self.assertNotIn("odml:Cell", rdf_writer.get_rdf_str()) + self.assertIn("odml:Neuron", rdf_writer.get_rdf_str()) From 143519e1244ab15af97aa32ee6c3fb282b39420a Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Tue, 14 Jul 2020 12:25:03 +0200 Subject: [PATCH 05/11] [tools/RDFWriter] Add subClassOf usage Closes #396. --- odml/tools/rdf_converter.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/odml/tools/rdf_converter.py b/odml/tools/rdf_converter.py index a34c03af..28e925b1 100644 --- a/odml/tools/rdf_converter.py +++ b/odml/tools/rdf_converter.py @@ -10,7 +10,7 @@ from io import StringIO from rdflib import Graph, Literal, URIRef from rdflib.graph import Seq -from rdflib.namespace import XSD, RDF +from rdflib.namespace import XSD, RDF, RDFS import yaml @@ -252,6 +252,9 @@ def save_section(self, sec, curr_node): sub_sec = self._get_section_subclass(sec) if sub_sec: curr_type = sub_sec + self.graph.add((URIRef(fmt.rdf_type), RDF.type, RDFS.Class)) + self.graph.add((URIRef(curr_type), RDF.type, RDFS.Class)) + self.graph.add((URIRef(curr_type), RDFS.subClassOf, URIRef(fmt.rdf_type))) self.graph.add((curr_node, RDF.type, URIRef(curr_type))) From 5b6462ae2a183bce6800a719c12e79889c9c72bf Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Tue, 14 Jul 2020 12:25:40 +0200 Subject: [PATCH 06/11] [test/RDFWriter] Add subClassOf tests --- test/test_rdf_writer.py | 166 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 2 deletions(-) diff --git a/test/test_rdf_writer.py b/test/test_rdf_writer.py index de40356d..db23ada3 100644 --- a/test/test_rdf_writer.py +++ b/test/test_rdf_writer.py @@ -4,13 +4,16 @@ import yaml +from owlrl import DeductiveClosure, RDFS_Semantics + from rdflib import URIRef, Literal -from rdflib.namespace import XSD, RDF +from rdflib.namespace import Namespace, RDF, RDFS, XSD +from rdflib.plugins.sparql import prepareQuery import odml from odml.format import Format -from odml.tools.rdf_converter import RDFWriter +from odml.tools.rdf_converter import ODML_NS, RDFWriter from .test_samplefile import SampleFileCreator from .test_samplefile import parse @@ -312,6 +315,11 @@ def test_rdf_subclassing_switch(self): self.assertNotIn("odml:Cell", result) def test_rdf_custom_subclasses(self): + """ + Test collection of the odml RDF subclassing feature. + Tests that the resulting output RDF document contains any required + additional RDF subclasses. + """ sub_class_term = "cell" # Create minimal document @@ -354,3 +362,157 @@ def test_rdf_custom_subclasses(self): rdf_writer = RDFWriter([doc], custom_subclasses=custom_class_dict) self.assertNotIn("odml:Cell", rdf_writer.get_rdf_str()) self.assertIn("odml:Neuron", rdf_writer.get_rdf_str()) + + def test_rdf_subclassing_definitions(self): + """ + Test that RDF Subclass definitions are written to the resulting graph. + """ + # -- Test default subclassing + doc = odml.Document() + _ = odml.Section(name="test_subclassing", type="cell", parent=doc) + + rdf_writer = RDFWriter([doc]) + curr_str = " ".join(rdf_writer.get_rdf_str().split()) + self.assertIn("odml:Cell a rdfs:Class ; rdfs:subClassOf odml:Section", curr_str) + self.assertIn("odml:Section a rdfs:Class", curr_str) + + # -- Test multiple entries; a definition should only occur once in an RDF document + doc = odml.Document() + sec = odml.Section(name="test_subclassing", type="cell", parent=doc) + sub_sec = odml.Section(name="test_subclassing", type="cell", parent=sec) + _ = odml.Section(name="test_subclassing", type="cell", parent=sub_sec) + + rdf_writer = RDFWriter([doc]) + curr_str = " ".join(rdf_writer.get_rdf_str().split()) + self.assertIn("odml:Cell a rdfs:Class ; rdfs:subClassOf odml:Section", curr_str) + self.assertIs(curr_str.count("odml:Cell a rdfs:Class ; rdfs:subClassOf odml:Section"), 1) + self.assertIn("odml:Section a rdfs:Class", curr_str) + self.assertIs(curr_str.count("odml:Section a rdfs:Class"), 1) + + # -- Test custom subclassing + type_custom_class = "species" + custom_class_dict = {type_custom_class: "Species"} + + doc = odml.Document() + _ = odml.Section(name="test_subclassing", type="cell", parent=doc) + _ = odml.Section(name="test_custom_subclassing", type=type_custom_class, parent=doc) + + rdf_writer = RDFWriter([doc], custom_subclasses=custom_class_dict) + curr_str = " ".join(rdf_writer.get_rdf_str().split()) + self.assertIn("odml:Cell a rdfs:Class ; rdfs:subClassOf odml:Section", curr_str) + self.assertIn("odml:Species a rdfs:Class ; rdfs:subClassOf odml:Section", curr_str) + self.assertIn("odml:Section a rdfs:Class", curr_str) + + # -- Test inactive subclassing + doc = odml.Document() + _ = odml.Section(name="test_subclassing", type="cell", parent=doc) + + rdf_writer = RDFWriter([doc], rdf_subclassing=False) + curr_str = " ".join(rdf_writer.get_rdf_str().split()) + self.assertNotIn("odml:Section a rdfs:Class", curr_str) + self.assertNotIn("odml:Cell a rdfs:Class ; rdfs:subClassOf odml:Section", curr_str) + + def test_rdf_subclassing_queries(self): + """ + Test the proper implementation of the RDF subclassing feature. Tests ensure, that queries + relying on RDF Subclasses return appropriate results. + """ + namespace_map = {"odml": Namespace(ODML_NS), "rdf": RDF, "rdfs": RDFS} + + doc = odml.Document() + _ = odml.Section(name="test_subclass", type="cell", parent=doc) + _ = odml.Section(name="test_regular_class", type="regular", parent=doc) + + rdf_writer = RDFWriter([doc]) + _ = rdf_writer.get_rdf_str() + + use_graph = rdf_writer.graph + DeductiveClosure(RDFS_Semantics).expand(use_graph) + + q_string = "SELECT * WHERE {?s rdf:type odml:Section .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) + + # Make sure the query finds two sections + self.assertIs(len(use_graph.query(curr_query)), 2) + + # Make sure the query finds + result_section = [] + for row in use_graph.query(curr_query): + result_section.append(row.s) + + q_string = "SELECT * WHERE {?s rdf:type odml:Cell .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) + + self.assertIs(len(use_graph.query(curr_query)), 1) + for row in use_graph.query(curr_query): + self.assertIn(row.s, result_section) + + # -- Test custom subclassing queries + type_custom_class = "species" + type_overwrite_class = "cell" + custom_class_dict = {type_custom_class: "Species", type_overwrite_class: "Neuron"} + + doc = odml.Document() + sec = odml.Section(name="test_subclass", type="species", parent=doc) + _ = odml.Section(name="test_subclass_overwrite", type="cell", parent=sec) + _ = odml.Section(name="test_regular_class", type="regular", parent=sec) + + rdf_writer = RDFWriter([doc], custom_subclasses=custom_class_dict) + _ = rdf_writer.get_rdf_str() + + use_graph = rdf_writer.graph + DeductiveClosure(RDFS_Semantics).expand(use_graph) + + q_string = "SELECT * WHERE {?s rdf:type odml:Section .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) + + # Make sure the query finds three sections + self.assertIs(len(use_graph.query(curr_query)), 3) + + # Make sure the query finds + result_section = [] + for row in use_graph.query(curr_query): + result_section.append(row.s) + + # Custom class 'Species' should be found. + q_string = "SELECT * WHERE {?s rdf:type odml:Species .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) + + self.assertIs(len(use_graph.query(curr_query)), 1) + for row in use_graph.query(curr_query): + self.assertIn(row.s, result_section) + + # Custom class 'Neuron' should be found. + q_string = "SELECT * WHERE {?s rdf:type odml:Neuron .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) + + self.assertIs(len(use_graph.query(curr_query)), 1) + for row in use_graph.query(curr_query): + self.assertIn(row.s, result_section) + + # Default class 'Cell' was replaced and should not return any result. + q_string = "SELECT * WHERE {?s rdf:type odml:Cell .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) + + self.assertIs(len(use_graph.query(curr_query)), 0) + + # -- Test inactivated subclassing + doc = odml.Document() + _ = odml.Section(name="test_regular_class", type="regular", parent=doc) + _ = odml.Section(name="test_subclass", type="cell", parent=doc) + + rdf_writer = RDFWriter([doc], rdf_subclassing=False) + _ = rdf_writer.get_rdf_str() + + use_graph = rdf_writer.graph + DeductiveClosure(RDFS_Semantics).expand(use_graph) + + q_string = "SELECT * WHERE {?s rdf:type odml:Section .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) + + self.assertIs(len(use_graph.query(curr_query)), 2) + + q_string = "SELECT * WHERE {?s rdf:type odml:Cell .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) + + self.assertIs(len(use_graph.query(curr_query)), 0) From 275dbe84f03e679288e8c7108b00af874f429344 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Tue, 14 Jul 2020 12:55:36 +0200 Subject: [PATCH 07/11] [setup] Add 'owlrl' dependency --- appveyor.yml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 8cc7906b..279e5dc9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -37,7 +37,7 @@ init: build: false install: - - python -m pip install lxml enum34 pyyaml rdflib + - python -m pip install lxml enum34 pyyaml rdflib owlrl test_script: - python --version diff --git a/setup.py b/setup.py index 9418eda0..0b4db426 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ with open('README.md') as f: description_text = f.read() -install_req = ["lxml", "pyyaml>=5.1", "rdflib", "docopt", "pathlib"] +install_req = ["lxml", "pyyaml>=5.1", "rdflib", "docopt", "pathlib", "owlrl"] if sys.version_info < (3, 4): install_req += ["enum34"] From 3ab658cd21ba2fc215bb9b3f3ae4e884e0098789 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Tue, 14 Jul 2020 14:19:21 +0200 Subject: [PATCH 08/11] [test/RDFWriter] Py2 compatibility Hopefully for the last time --- test/test_rdf_writer.py | 157 +++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 76 deletions(-) diff --git a/test/test_rdf_writer.py b/test/test_rdf_writer.py index db23ada3..08f7df0b 100644 --- a/test/test_rdf_writer.py +++ b/test/test_rdf_writer.py @@ -2,9 +2,12 @@ import os import unittest +from sys import version_info + import yaml -from owlrl import DeductiveClosure, RDFS_Semantics +if version_info > (3, 4): + from owlrl import DeductiveClosure, RDFS_Semantics from rdflib import URIRef, Literal from rdflib.namespace import Namespace, RDF, RDFS, XSD @@ -358,10 +361,11 @@ def test_rdf_custom_subclasses(self): doc = odml.Document() _ = odml.Section(name="test_subclassing", type=sub_class_type, parent=doc) - with self.assertWarns(UserWarning): - rdf_writer = RDFWriter([doc], custom_subclasses=custom_class_dict) - self.assertNotIn("odml:Cell", rdf_writer.get_rdf_str()) - self.assertIn("odml:Neuron", rdf_writer.get_rdf_str()) + if version_info > (3, 4): + with self.assertWarns(UserWarning): + rdf_writer = RDFWriter([doc], custom_subclasses=custom_class_dict) + self.assertNotIn("odml:Cell", rdf_writer.get_rdf_str()) + self.assertIn("odml:Neuron", rdf_writer.get_rdf_str()) def test_rdf_subclassing_definitions(self): """ @@ -417,102 +421,103 @@ def test_rdf_subclassing_queries(self): Test the proper implementation of the RDF subclassing feature. Tests ensure, that queries relying on RDF Subclasses return appropriate results. """ - namespace_map = {"odml": Namespace(ODML_NS), "rdf": RDF, "rdfs": RDFS} + if version_info > (3, 4): + namespace_map = {"odml": Namespace(ODML_NS), "rdf": RDF, "rdfs": RDFS} - doc = odml.Document() - _ = odml.Section(name="test_subclass", type="cell", parent=doc) - _ = odml.Section(name="test_regular_class", type="regular", parent=doc) + doc = odml.Document() + _ = odml.Section(name="test_subclass", type="cell", parent=doc) + _ = odml.Section(name="test_regular_class", type="regular", parent=doc) - rdf_writer = RDFWriter([doc]) - _ = rdf_writer.get_rdf_str() + rdf_writer = RDFWriter([doc]) + _ = rdf_writer.get_rdf_str() - use_graph = rdf_writer.graph - DeductiveClosure(RDFS_Semantics).expand(use_graph) + use_graph = rdf_writer.graph + DeductiveClosure(RDFS_Semantics).expand(use_graph) - q_string = "SELECT * WHERE {?s rdf:type odml:Section .}" - curr_query = prepareQuery(q_string, initNs=namespace_map) + q_string = "SELECT * WHERE {?s rdf:type odml:Section .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) - # Make sure the query finds two sections - self.assertIs(len(use_graph.query(curr_query)), 2) + # Make sure the query finds two sections + self.assertIs(len(use_graph.query(curr_query)), 2) - # Make sure the query finds - result_section = [] - for row in use_graph.query(curr_query): - result_section.append(row.s) + # Make sure the query finds + result_section = [] + for row in use_graph.query(curr_query): + result_section.append(row.s) - q_string = "SELECT * WHERE {?s rdf:type odml:Cell .}" - curr_query = prepareQuery(q_string, initNs=namespace_map) + q_string = "SELECT * WHERE {?s rdf:type odml:Cell .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) - self.assertIs(len(use_graph.query(curr_query)), 1) - for row in use_graph.query(curr_query): - self.assertIn(row.s, result_section) + self.assertIs(len(use_graph.query(curr_query)), 1) + for row in use_graph.query(curr_query): + self.assertIn(row.s, result_section) - # -- Test custom subclassing queries - type_custom_class = "species" - type_overwrite_class = "cell" - custom_class_dict = {type_custom_class: "Species", type_overwrite_class: "Neuron"} + # -- Test custom subclassing queries + type_custom_class = "species" + type_overwrite_class = "cell" + custom_class_dict = {type_custom_class: "Species", type_overwrite_class: "Neuron"} - doc = odml.Document() - sec = odml.Section(name="test_subclass", type="species", parent=doc) - _ = odml.Section(name="test_subclass_overwrite", type="cell", parent=sec) - _ = odml.Section(name="test_regular_class", type="regular", parent=sec) + doc = odml.Document() + sec = odml.Section(name="test_subclass", type="species", parent=doc) + _ = odml.Section(name="test_subclass_overwrite", type="cell", parent=sec) + _ = odml.Section(name="test_regular_class", type="regular", parent=sec) - rdf_writer = RDFWriter([doc], custom_subclasses=custom_class_dict) - _ = rdf_writer.get_rdf_str() + rdf_writer = RDFWriter([doc], custom_subclasses=custom_class_dict) + _ = rdf_writer.get_rdf_str() - use_graph = rdf_writer.graph - DeductiveClosure(RDFS_Semantics).expand(use_graph) + use_graph = rdf_writer.graph + DeductiveClosure(RDFS_Semantics).expand(use_graph) - q_string = "SELECT * WHERE {?s rdf:type odml:Section .}" - curr_query = prepareQuery(q_string, initNs=namespace_map) + q_string = "SELECT * WHERE {?s rdf:type odml:Section .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) - # Make sure the query finds three sections - self.assertIs(len(use_graph.query(curr_query)), 3) + # Make sure the query finds three sections + self.assertIs(len(use_graph.query(curr_query)), 3) - # Make sure the query finds - result_section = [] - for row in use_graph.query(curr_query): - result_section.append(row.s) + # Make sure the query finds + result_section = [] + for row in use_graph.query(curr_query): + result_section.append(row.s) - # Custom class 'Species' should be found. - q_string = "SELECT * WHERE {?s rdf:type odml:Species .}" - curr_query = prepareQuery(q_string, initNs=namespace_map) + # Custom class 'Species' should be found. + q_string = "SELECT * WHERE {?s rdf:type odml:Species .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) - self.assertIs(len(use_graph.query(curr_query)), 1) - for row in use_graph.query(curr_query): - self.assertIn(row.s, result_section) + self.assertIs(len(use_graph.query(curr_query)), 1) + for row in use_graph.query(curr_query): + self.assertIn(row.s, result_section) - # Custom class 'Neuron' should be found. - q_string = "SELECT * WHERE {?s rdf:type odml:Neuron .}" - curr_query = prepareQuery(q_string, initNs=namespace_map) + # Custom class 'Neuron' should be found. + q_string = "SELECT * WHERE {?s rdf:type odml:Neuron .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) - self.assertIs(len(use_graph.query(curr_query)), 1) - for row in use_graph.query(curr_query): - self.assertIn(row.s, result_section) + self.assertIs(len(use_graph.query(curr_query)), 1) + for row in use_graph.query(curr_query): + self.assertIn(row.s, result_section) - # Default class 'Cell' was replaced and should not return any result. - q_string = "SELECT * WHERE {?s rdf:type odml:Cell .}" - curr_query = prepareQuery(q_string, initNs=namespace_map) + # Default class 'Cell' was replaced and should not return any result. + q_string = "SELECT * WHERE {?s rdf:type odml:Cell .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) - self.assertIs(len(use_graph.query(curr_query)), 0) + self.assertIs(len(use_graph.query(curr_query)), 0) - # -- Test inactivated subclassing - doc = odml.Document() - _ = odml.Section(name="test_regular_class", type="regular", parent=doc) - _ = odml.Section(name="test_subclass", type="cell", parent=doc) + # -- Test inactivated subclassing + doc = odml.Document() + _ = odml.Section(name="test_regular_class", type="regular", parent=doc) + _ = odml.Section(name="test_subclass", type="cell", parent=doc) - rdf_writer = RDFWriter([doc], rdf_subclassing=False) - _ = rdf_writer.get_rdf_str() + rdf_writer = RDFWriter([doc], rdf_subclassing=False) + _ = rdf_writer.get_rdf_str() - use_graph = rdf_writer.graph - DeductiveClosure(RDFS_Semantics).expand(use_graph) + use_graph = rdf_writer.graph + DeductiveClosure(RDFS_Semantics).expand(use_graph) - q_string = "SELECT * WHERE {?s rdf:type odml:Section .}" - curr_query = prepareQuery(q_string, initNs=namespace_map) + q_string = "SELECT * WHERE {?s rdf:type odml:Section .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) - self.assertIs(len(use_graph.query(curr_query)), 2) + self.assertIs(len(use_graph.query(curr_query)), 2) - q_string = "SELECT * WHERE {?s rdf:type odml:Cell .}" - curr_query = prepareQuery(q_string, initNs=namespace_map) + q_string = "SELECT * WHERE {?s rdf:type odml:Cell .}" + curr_query = prepareQuery(q_string, initNs=namespace_map) - self.assertIs(len(use_graph.query(curr_query)), 0) + self.assertIs(len(use_graph.query(curr_query)), 0) From 3197ccc011ff57fda178ddc66442d75ca51d7027 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Tue, 14 Jul 2020 14:20:18 +0200 Subject: [PATCH 09/11] [setup] Add test specific dependencies --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0b4db426..0250e7fa 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,9 @@ with open('README.md') as f: description_text = f.read() -install_req = ["lxml", "pyyaml>=5.1", "rdflib", "docopt", "pathlib", "owlrl"] +install_req = ["lxml", "pyyaml>=5.1", "rdflib", "docopt", "pathlib"] + +tests_req = ["owlrl", "requests"] if sys.version_info < (3, 4): install_req += ["enum34"] @@ -45,6 +47,7 @@ packages=packages, test_suite='test', install_requires=install_req, + tests_require=tests_req, include_package_data=True, long_description=description_text, long_description_content_type="text/markdown", From ce5f19604bb900a3e2557f27315b77b32c1bda0c Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Tue, 14 Jul 2020 14:20:46 +0200 Subject: [PATCH 10/11] [appveyor/travis] Add test dependencies --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 78c0d47b..64ce8763 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,7 +75,7 @@ install: export PIPCMD=pip; fi; - - $PIPCMD install lxml enum34 pyyaml rdflib + - $PIPCMD install lxml enum34 pyyaml rdflib owlrl requests script: - which $PYCMD diff --git a/appveyor.yml b/appveyor.yml index 279e5dc9..b4717893 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -37,7 +37,7 @@ init: build: false install: - - python -m pip install lxml enum34 pyyaml rdflib owlrl + - python -m pip install lxml enum34 pyyaml rdflib owlrl requests test_script: - python --version From 41d72b082ef5c1ba41de4c53abc51d7a3454efa4 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 16 Jul 2020 09:28:47 +0200 Subject: [PATCH 11/11] [tools/RDFWriter] Exclude all subclass whitespace In homage to the unknown DAU all whitespaces are excluded from the custom RDF subclasses dict. --- odml/tools/rdf_converter.py | 8 +++++--- test/test_rdf_writer.py | 6 +++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/odml/tools/rdf_converter.py b/odml/tools/rdf_converter.py index 28e925b1..30d370a6 100644 --- a/odml/tools/rdf_converter.py +++ b/odml/tools/rdf_converter.py @@ -4,6 +4,7 @@ """ import os +import string import uuid import warnings @@ -338,9 +339,10 @@ def _parse_custom_subclasses(self, custom_subclasses): otherwise. """ - # Do not allow whitespaces in values - if " " in "".join(custom_subclasses.values()): - msg = "Custom RDF Subclass names must not contain any whitespaces." + # Do not allow any whitespace characters in values + vals = "".join(custom_subclasses.values()).encode() + if vals != vals.translate(None, string.whitespace.encode()): + msg = "Custom RDF Subclass names must not contain any whitespace characters." raise ValueError(msg) for k in custom_subclasses: diff --git a/test/test_rdf_writer.py b/test/test_rdf_writer.py index 08f7df0b..87448e4b 100644 --- a/test/test_rdf_writer.py +++ b/test/test_rdf_writer.py @@ -338,7 +338,11 @@ def test_rdf_custom_subclasses(self): self.assertIn("odml:Cell", rdf_writer.get_rdf_str()) # Test value whitespace - invalid_dict = {"type_1": "Class 1", "type_2": "Class 2"} + inval_a = "This should" + inval_b = "fail\nin" + inval_c = "the\tmost" + inval_d = "complete\rway" + invalid_dict = {"type_1": inval_a, "type_2": inval_b, "type_3": inval_c, "type_4": inval_d} with self.assertRaises(ValueError): _ = RDFWriter([doc], custom_subclasses=invalid_dict)