diff --git a/.gitignore b/.gitignore index 8a33731..64b074c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.pyc .not/ +.ropeproject/ diff --git a/README.md b/README.md index ee7bfe7..db29c64 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Commands: f # Go forward in history b # Go back in history hist # Print history stack. + select # SPARQL SELECT query, example: select distinct ?p where { ?s ?p ?o } order by ?p help # Print this help. exit # Exit. diff --git a/rdfcli/controller.py b/rdfcli/controller.py index 5bceef3..8e592b4 100644 --- a/rdfcli/controller.py +++ b/rdfcli/controller.py @@ -1,3 +1,5 @@ +import operator + from history import History class Controller: @@ -129,3 +131,17 @@ def types(self): def norm(self, ref): return self.model.norm(ref) + def select(self, query): + cols = None + rows = [] + sortkey = operator.itemgetter(1) + for row in self.model.select(query): + values = [] + if cols is None: + cols = [ + col for col, _ in sorted(row.labels.items(), key=sortkey) + ] + for col in cols: + values.append(self.norm(row[col])) + rows.append(values) + return cols, rows diff --git a/rdfcli/model.py b/rdfcli/model.py index 602fc92..35c00e0 100644 --- a/rdfcli/model.py +++ b/rdfcli/model.py @@ -59,7 +59,12 @@ def get_reverse_properties(self, obj): return properties def norm(self, ref): - return self.graph.namespace_manager.normalizeUri(ref) if ref else None + if ref is None: + return None + elif isinstance(ref, URIRef): + return self.graph.namespace_manager.normalizeUri(ref) + else: + return unicode(ref) def to_uriref(self, string): """Expand QName to UriRef based on existing namespaces.""" @@ -75,4 +80,5 @@ def to_uriref(self, string): else: return URIRef(string) - + def select(self, query): + return self.graph.query(query) diff --git a/rdfcli/view.py b/rdfcli/view.py index 09770e0..f707153 100644 --- a/rdfcli/view.py +++ b/rdfcli/view.py @@ -1,4 +1,6 @@ +import pyparsing from cmd import Cmd +from prettytable import PrettyTable HELP_MSG = ''' Commands: @@ -16,6 +18,7 @@ f # Go forward in history b # Go back in history hist # Print history stack. + select # SPARQL SELECT query, example: select distinct ?p where { ?s ?p ?o } order by ?p help # Print this help. exit # Exit. ''' @@ -148,6 +151,18 @@ def do_types(self, params): for type_ in types: print self.__norm(type_) + def do_select(self, params): + try: + cols, rows = self.controller.select('select %s' % params) + except pyparsing.ParseException as e: + print e + else: + table = PrettyTable(cols) + table.align = 'l' + for row in rows: + table.add_row(row) + print table + def __norm(self, ref, trim=False): res = self.controller.norm(ref) if trim and res[0] == '<' and res[-1] == '>': diff --git a/requirements.txt b/requirements.txt index 10b9c0b..053ff5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ pyparsing==1.5.7 rdflib==4.0.1 six==1.4.1 wsgiref==0.1.2 +prettytable==0.7.2 diff --git a/test/test_controller.py b/test/test_controller.py index 4d871a0..3530f78 100644 --- a/test/test_controller.py +++ b/test/test_controller.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals + import unittest -from rdflib import Graph from rdflib.namespace import Namespace, RDF, FOAF -from rdflib.term import URIRef, Literal +from rdflib.term import Literal from rdfcli.controller import Controller from rdfcli.model import Model @@ -11,18 +12,17 @@ N = Namespace('http://example.org/#') REL = Namespace('http://www.perceive.net/schemas/relationship/') + +class FakeModel(Model): + def load(self, source, format=None): + self.graph.parse('test/fixture.rdf') + + class TestController(unittest.TestCase): def setUp(self): self.controller = Controller() - - # stub Model.load to load only a local file - def load_stub(self, source, format=None): - self.graph.parse('test/fixture.rdf') - - Model.load = load_stub - model = Model() - self.controller.set_model(model) + self.controller.set_model(FakeModel()) self.controller.load('test/fixture.rdf') def test_initialization(self): @@ -38,10 +38,10 @@ def test_ls(self): result = self.controller.ls(None) self.assertIn(FOAF.Person, result[RDF.type]) self.assertIn(Literal('Spiderman'), result[FOAF.name]) - self.assertIn(Literal(u'Человек-паук', lang=u'ru'), result[FOAF.name]) + self.assertIn(Literal('Человек-паук', lang='ru'), result[FOAF.name]) result = self.controller.ls('foaf:name') self.assertIn(Literal('Spiderman'), result) - self.assertIn(Literal(u'Человек-паук', lang=u'ru'), result) + self.assertIn(Literal('Человек-паук', lang='ru'), result) def test_is(self): self.controller.go(N.spiderman) @@ -73,16 +73,30 @@ def test_bw(self): def test_forward(self): self.controller.go(N.spiderman) self.controller.back() - result = self.controller.forward() + self.controller.forward() self.assertEqual(N.spiderman, self.controller.current) def test_back(self): self.controller.go(N.spiderman) - result = self.controller.back() + self.controller.back() self.assertEqual(None, self.controller.current) def test_this(self): self.controller.go(N.spiderman) - result = self.controller.this() + self.controller.this() self.assertEqual(N.spiderman, self.controller.current) + def test_select(self): + self.controller.go(N.spiderman) + self.controller.model.graph.bind('ex', 'http://example.org/#') + cols, rows = self.controller.select('select ?s ?p ?o where { ?s ?p ?o . }') + self.assertEqual(cols, ['s', 'p', 'o']) + self.assertEqual(sorted(rows), [ + ['ex:green_goblin', 'foaf:name', 'Green Goblin'], + ['ex:green_goblin', 'rdf:type', 'foaf:Person'], + ['ex:green_goblin', 'rel:enemyOf', 'ex:spiderman'], + ['ex:spiderman', 'foaf:name', 'Spiderman'], + ['ex:spiderman', 'foaf:name', 'Человек-паук'], + ['ex:spiderman', 'rdf:type', 'foaf:Person'], + ['ex:spiderman', 'rel:enemyOf', 'ex:green_goblin'], + ])