From c196d2ce73d8351a18c19bcddd4b06d224e644fc Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sat, 21 Jan 2023 18:27:22 +0100 Subject: Fetch in graph including results view --- test/graph/test_nodes.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 4 deletions(-) (limited to 'test/graph/test_nodes.py') diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py index 2870f35..a4e07ee 100644 --- a/test/graph/test_nodes.py +++ b/test/graph/test_nodes.py @@ -9,8 +9,8 @@ import rdflib import unittest # bsie imports -from bsfs import schema as _schema -from bsfs.namespace import ns +from bsfs import schema as bsc +from bsfs.namespace import Namespace, ns from bsfs.triple_store.sparql import SparqlStore from bsfs.utils import errors, URI @@ -20,11 +20,13 @@ from bsfs.graph.nodes import Nodes ## code ## +bst = Namespace('http://bsfs.ai/schema/Tag') + class TestNodes(unittest.TestCase): def setUp(self): # initialize backend self.backend = SparqlStore() - self.backend.schema = _schema.from_string(''' + self.backend.schema = bsc.from_string(''' prefix rdfs: prefix xsd: @@ -67,6 +69,11 @@ class TestNodes(unittest.TestCase): rdfs:range bsfs:User ; bsfs:unique "true"^^xsd:boolean . + bst:label rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range xsd:string ; + bsfs:unique "true"^^xsd:boolean . + bst:representative rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Tag ; rdfs:range bsfs:Entity ; @@ -89,7 +96,8 @@ class TestNodes(unittest.TestCase): (rdflib.URIRef(ns.bse.filesize), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), (rdflib.URIRef(ns.bse.tag), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), (rdflib.URIRef(ns.bse.author), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), - (rdflib.URIRef('http://bsfs.ai/schema/Tag#representative'), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), + (rdflib.URIRef(bst.representative), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), + (rdflib.URIRef(bst.label), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), } # Nodes constructor args self.user = URI('http://example.com/me') @@ -371,6 +379,85 @@ class TestNodes(unittest.TestCase): (self.p_author.uri, Nodes(self.backend, self.user, self.user_type, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')})))) self.assertSetEqual(curr, set(self.backend._graph)) + def test_fetch(self): + # setup: add some instances + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}) \ + .set(ns.bse.comment, 'hello world') \ + .set(ns.bse.filesize, 1234) \ + .set(ns.bse.tag, Nodes(self.backend, self.user, self.tag_type, {'http://example.com/me/tag#1234'})) + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}) \ + .set(ns.bse.filesize, 4321) \ + .set(ns.bse.tag, Nodes(self.backend, self.user, self.tag_type, {'http://example.com/me/tag#4321'})) + Nodes(self.backend, self.user, self.tag_type, {'http://example.com/me/tag#1234'}) \ + .set(bst.label, 'tag_label_1234') + Nodes(self.backend, self.user, self.tag_type, {'http://example.com/me/tag#4321'}) \ + .set(bst.label, 'tag_label_4321') + # setup: get nodes instance + nodes = Nodes(self.backend, self.user, self.ent_type, self.ent_ids) + # must pass at least one path + self.assertRaises(AttributeError, nodes.get) + # view must be list or dict + self.assertRaises(ValueError, nodes.get, ns.bse.filesize, view='hello') + self.assertRaises(ValueError, nodes.get, ns.bse.filesize, view=1234) + self.assertRaises(ValueError, nodes.get, ns.bse.filesize, view=tuple) + # can pass path as URI + self.assertDictEqual(nodes.get(ns.bse.filesize), { + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}): 1234, + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}): 4321, + }) + # can pass path as sequence of URI + self.assertDictEqual(nodes.get((ns.bse.tag, bst.label)), { + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}): {'tag_label_1234'}, + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}): {'tag_label_4321'}, + }) + # get returns the same path that was passed + self.assertCountEqual(list(nodes.get((ns.bse.tag, bst.label), path=False, view=list)), [ + (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}), (ns.bse.tag, bst.label), 'tag_label_1234'), + (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}), (ns.bse.tag, bst.label), 'tag_label_4321'), + ]) + self.assertCountEqual(list(nodes.get([ns.bse.tag, bst.label], path=False, view=list)), [ + (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}), [ns.bse.tag, bst.label], 'tag_label_1234'), + (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}), [ns.bse.tag, bst.label], 'tag_label_4321'), + ]) + # paths must be URI or sequence thereof + self.assertRaises(TypeError, nodes.get, 1234) + self.assertRaises(TypeError, nodes.get, (ns.bse.tag, 1234)) + self.assertRaises(TypeError, nodes.get, (1234, ns.bse.tag)) + self.assertRaises(errors.ConsistencyError, nodes.get, 'hello world') + self.assertRaises(errors.ConsistencyError, nodes.get, ns.bse.invalid) + self.assertRaises(errors.ConsistencyError, nodes.get, (ns.bse.tag, bst.invalid)) + # can pass multiple paths + self.assertDictEqual(nodes.get(ns.bse.filesize, (ns.bse.tag, bst.label)), { + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}): { + ns.bse.filesize: 1234, + (ns.bse.tag, bst.label): {'tag_label_1234'}, + }, + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}): { + ns.bse.filesize: 4321, + (ns.bse.tag, bst.label): {'tag_label_4321'}, + }, + }) + # get respects view + self.assertDictEqual(nodes.get(ns.bse.filesize, view=dict), { + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}): 1234, + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}): 4321, + }) + self.assertSetEqual(set(nodes.get(ns.bse.filesize, view=list)), { + (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}), 1234), + (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}), 4321), + }) + # get returns Nodes instance when fetching a node + self.assertDictEqual(nodes.get(ns.bse.tag), { + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}): + {Nodes(self.backend, self.user, self.tag_type, {'http://example.com/me/tag#1234'})}, + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}): + {Nodes(self.backend, self.user, self.tag_type, {'http://example.com/me/tag#4321'})}, + }) + # get returns a value when fetching a value and omits missing values + self.assertDictEqual(nodes.get(ns.bse.comment), { + Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}): {'hello world'}, + }) + ## main ## -- cgit v1.2.3 From 9310610a7edf4dcbb934aedcecff1d11348197bb Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sat, 21 Jan 2023 22:32:33 +0100 Subject: nodes predicate walk sugar --- test/graph/test_nodes.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'test/graph/test_nodes.py') diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py index a4e07ee..670df69 100644 --- a/test/graph/test_nodes.py +++ b/test/graph/test_nodes.py @@ -10,6 +10,7 @@ import unittest # bsie imports from bsfs import schema as bsc +from bsfs.graph.walk import Walk from bsfs.namespace import Namespace, ns from bsfs.triple_store.sparql import SparqlStore from bsfs.utils import errors, URI @@ -108,7 +109,8 @@ class TestNodes(unittest.TestCase): self.p_filesize = self.backend.schema.predicate(ns.bse.filesize) self.p_author = self.backend.schema.predicate(ns.bse.author) self.p_tag = self.backend.schema.predicate(ns.bse.tag) - self.p_representative = self.backend.schema.predicate(URI('http://bsfs.ai/schema/Tag#representative')) + self.p_representative = self.backend.schema.predicate(bst.representative) + self.p_label = self.backend.schema.predicate(bst.label) self.t_created = self.backend.schema.predicate(ns.bsm.t_created) self.ent_ids = { URI('http://example.com/me/entity#1234'), @@ -458,6 +460,16 @@ class TestNodes(unittest.TestCase): Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}): {'hello world'}, }) + def test_getattr(self): + nodes = Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}) + # can get walks to values + self.assertEqual(nodes.filesize, Walk(nodes, (self.p_filesize, ))) + # can get walks to nodes + self.assertEqual(nodes.tag, Walk(nodes, (self.p_tag, ))) + # can do multiple hops + self.assertEqual(nodes.tag.label, Walk(nodes, (self.p_tag, self.p_label))) + # invalid step raises an error + self.assertRaises(ValueError, getattr, nodes, 'foobar') ## main ## -- cgit v1.2.3 From 04bb201c6162e81dbdefcb1cff9595180fa66917 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sat, 21 Jan 2023 22:34:49 +0100 Subject: minor notes --- test/graph/test_nodes.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'test/graph/test_nodes.py') diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py index 670df69..4eae250 100644 --- a/test/graph/test_nodes.py +++ b/test/graph/test_nodes.py @@ -381,7 +381,7 @@ class TestNodes(unittest.TestCase): (self.p_author.uri, Nodes(self.backend, self.user, self.user_type, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')})))) self.assertSetEqual(curr, set(self.backend._graph)) - def test_fetch(self): + def test_get(self): # setup: add some instances Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}) \ .set(ns.bse.comment, 'hello world') \ @@ -460,6 +460,11 @@ class TestNodes(unittest.TestCase): Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}): {'hello world'}, }) + # FIXME: What if I call `get` with a single predicate and a single node, but + # that node has no value for that predicate? + # so, essentially, what if triples is empty? -> Also check in test_result! + raise NotImplementedError() + def test_getattr(self): nodes = Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}) # can get walks to values -- cgit v1.2.3 From 1392951dfc82af05e7a5999baa1c0a4fc72083b8 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sat, 21 Jan 2023 23:03:50 +0100 Subject: Nodes magic methods for convenience --- test/graph/test_nodes.py | 109 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) (limited to 'test/graph/test_nodes.py') diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py index 4eae250..c07fa53 100644 --- a/test/graph/test_nodes.py +++ b/test/graph/test_nodes.py @@ -4,10 +4,14 @@ Part of the bsfs test suite. A copy of the license is provided with the project. Author: Matthias Baumgartner, 2022 """ -# imports -import rdflib +# standard imports +from functools import partial +import operator import unittest +# external imports +import rdflib + # bsie imports from bsfs import schema as bsc from bsfs.graph.walk import Walk @@ -476,6 +480,107 @@ class TestNodes(unittest.TestCase): # invalid step raises an error self.assertRaises(ValueError, getattr, nodes, 'foobar') + def test_schema(self): + self.assertEqual(Nodes(self.backend, self.user, self.ent_type, + {URI('http://example.com/me/entity#1234')}).schema, self.backend.schema) + + def test_operators(self): # __add__, __or__, __sub__, __and__ + gen = partial(Nodes, self.backend, self.user, self.ent_type) + nodes = gen({URI('http://example.com/me/entity#1234')}) + # add/or concatenates guids + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) + + gen({URI('http://example.com/me/entity#4321')}), + # target + gen({ + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321')})) + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) | + gen({URI('http://example.com/me/entity#4321')}), + # target + gen({ + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321')})) + # repeated guids are ignored + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) + + gen({URI('http://example.com/me/entity#1234')}), + # target + gen({URI('http://example.com/me/entity#1234')})) + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) | + gen({URI('http://example.com/me/entity#1234')}), + # target + gen({URI('http://example.com/me/entity#1234')})) + + # sub substracts guids + self.assertEqual( + gen({URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321')}) - + gen({URI('http://example.com/me/entity#4321')}), + # target + gen({URI('http://example.com/me/entity#1234')})) + # missing guids are ignored + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) - + gen({URI('http://example.com/me/entity#4321')}), + # target + gen({URI('http://example.com/me/entity#1234')})) + + # and intersects guids + self.assertEqual( + gen({URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321')}) & + gen({URI('http://example.com/me/entity#4321'), + URI('http://example.com/me/entity#5678')}), + # target + gen({URI('http://example.com/me/entity#4321')})) + + for op in (operator.add, operator.or_, operator.sub, operator.and_): + # type must match + self.assertRaises(TypeError, op, nodes, 1234) + self.assertRaises(TypeError, op, nodes, 'hello world') + # backend must match + self.assertRaises(ValueError, op, nodes, + Nodes(None, self.user, self.ent_type, {URI('http://example.com/me/entity#1234')})) + # user must match + self.assertRaises(ValueError, op, nodes, + Nodes(self.backend, '', self.ent_type, {URI('http://example.com/me/entity#1234')})) + # node type must match + self.assertRaises(ValueError, op, nodes, + Nodes(self.backend, self.user, self.tag_type, {URI('http://example.com/me/entity#1234')})) + + def test_len(self): + self.assertEqual(1, len(Nodes(self.backend, self.user, self.ent_type, { + URI('http://example.com/me/entity#1234'), + }))) + self.assertEqual(2, len(Nodes(self.backend, self.user, self.ent_type, { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321'), + }))) + self.assertEqual(4, len(Nodes(self.backend, self.user, self.ent_type, { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321'), + URI('http://example.com/me/entity#5678'), + URI('http://example.com/me/entity#8765'), + }))) + + def test_iter(self): # __iter__ + gen = partial(Nodes, self.backend, self.user, self.ent_type) + self.assertSetEqual(set(Nodes(self.backend, self.user, self.ent_type, { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321'), + URI('http://example.com/me/entity#5678'), + URI('http://example.com/me/entity#8765'), + })), { + gen({URI('http://example.com/me/entity#1234')}), + gen({URI('http://example.com/me/entity#4321')}), + gen({URI('http://example.com/me/entity#5678')}), + gen({URI('http://example.com/me/entity#8765')}), + }) + + ## main ## if __name__ == '__main__': -- cgit v1.2.3 From 72e0bd78dc9cc1d74c3061b028040b64c0efcf9f Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Mon, 30 Jan 2023 09:52:18 +0100 Subject: flip graph fethc result flags --- test/graph/test_nodes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test/graph/test_nodes.py') diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py index c07fa53..dabe794 100644 --- a/test/graph/test_nodes.py +++ b/test/graph/test_nodes.py @@ -417,11 +417,11 @@ class TestNodes(unittest.TestCase): Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}): {'tag_label_4321'}, }) # get returns the same path that was passed - self.assertCountEqual(list(nodes.get((ns.bse.tag, bst.label), path=False, view=list)), [ + self.assertCountEqual(list(nodes.get((ns.bse.tag, bst.label), path=True, view=list)), [ (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}), (ns.bse.tag, bst.label), 'tag_label_1234'), (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}), (ns.bse.tag, bst.label), 'tag_label_4321'), ]) - self.assertCountEqual(list(nodes.get([ns.bse.tag, bst.label], path=False, view=list)), [ + self.assertCountEqual(list(nodes.get([ns.bse.tag, bst.label], path=True, view=list)), [ (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#1234'}), [ns.bse.tag, bst.label], 'tag_label_1234'), (Nodes(self.backend, self.user, self.ent_type, {'http://example.com/me/entity#4321'}), [ns.bse.tag, bst.label], 'tag_label_4321'), ]) -- cgit v1.2.3 From cb819b8c268908b5f6cc680173db86e172847c46 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Wed, 8 Feb 2023 20:15:41 +0100 Subject: binary blob in schema and sparql triple store --- test/graph/test_nodes.py | 1 + 1 file changed, 1 insertion(+) (limited to 'test/graph/test_nodes.py') diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py index dabe794..6bb3ef3 100644 --- a/test/graph/test_nodes.py +++ b/test/graph/test_nodes.py @@ -92,6 +92,7 @@ class TestNodes(unittest.TestCase): (rdflib.URIRef(ns.bsfs.User), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Node)), (rdflib.URIRef(ns.xsd.string), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), (rdflib.URIRef(ns.bsfs.Array), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), + (rdflib.URIRef(ns.bsfs.BinaryBlob), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), (rdflib.URIRef(ns.bsfs.Feature), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Array)), (rdflib.URIRef(ns.bsfs.Number), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), (rdflib.URIRef(ns.bsfs.Time), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), -- cgit v1.2.3