iolanta 2.0.0__py3-none-any.whl → 2.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iolanta/cli/formatters/choose.py +1 -1
- iolanta/cli/formatters/csv.py +1 -1
- iolanta/cli/formatters/json.py +1 -1
- iolanta/cli/formatters/pretty.py +1 -2
- iolanta/cyberspace/processor.py +8 -3
- iolanta/data/textual-browser.yaml +9 -0
- iolanta/facets/facet.py +8 -9
- iolanta/facets/locator.py +25 -1
- iolanta/facets/textual_property_pairs_table.py +133 -0
- iolanta/iolanta.py +56 -13
- iolanta/query_result.py +73 -0
- {iolanta-2.0.0.dist-info → iolanta-2.0.2.dist-info}/METADATA +1 -1
- {iolanta-2.0.0.dist-info → iolanta-2.0.2.dist-info}/RECORD +15 -15
- ldflex/__init__.py +0 -1
- ldflex/ldflex.py +0 -138
- {iolanta-2.0.0.dist-info → iolanta-2.0.2.dist-info}/WHEEL +0 -0
- {iolanta-2.0.0.dist-info → iolanta-2.0.2.dist-info}/entry_points.txt +0 -0
iolanta/cli/formatters/choose.py
CHANGED
|
@@ -7,7 +7,7 @@ from iolanta.cli.formatters.json import print_json
|
|
|
7
7
|
from iolanta.cli.formatters.pretty import pretty_print
|
|
8
8
|
from iolanta.models import QueryResultsFormat
|
|
9
9
|
from iolanta.node_to_qname import node_to_qname
|
|
10
|
-
from
|
|
10
|
+
from iolanta.query_result import SelectResult
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
# @cli_print.instance(SelectResult)
|
iolanta/cli/formatters/csv.py
CHANGED
iolanta/cli/formatters/json.py
CHANGED
iolanta/cli/formatters/pretty.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import itertools
|
|
2
|
-
from datetime import date
|
|
3
2
|
from typing import Union
|
|
4
3
|
|
|
5
4
|
from classes import typeclass
|
|
@@ -10,7 +9,7 @@ from rich.table import Table
|
|
|
10
9
|
|
|
11
10
|
from iolanta.cli.pretty_print import render_literal_value
|
|
12
11
|
from iolanta.models import ComputedQName
|
|
13
|
-
from
|
|
12
|
+
from iolanta.query_result import QueryResult, SelectResult
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
@typeclass
|
iolanta/cyberspace/processor.py
CHANGED
|
@@ -111,7 +111,7 @@ def _extract_from_mapping( # noqa: WPS213
|
|
|
111
111
|
algebra: Mapping[str, Any],
|
|
112
112
|
) -> Iterable[URIRef | Variable]:
|
|
113
113
|
match algebra.name:
|
|
114
|
-
case 'SelectQuery' | 'Project' | 'Distinct':
|
|
114
|
+
case 'SelectQuery' | 'AskQuery' | 'Project' | 'Distinct':
|
|
115
115
|
yield from extract_mentioned_urls(algebra['p'])
|
|
116
116
|
|
|
117
117
|
case 'BGP':
|
|
@@ -260,7 +260,7 @@ def _extract_nanopublication_uris(
|
|
|
260
260
|
) -> Iterable[URIRef]:
|
|
261
261
|
"""Extract nanopublications to get retracting information for."""
|
|
262
262
|
match algebra.name:
|
|
263
|
-
case 'SelectQuery' | 'Project' | 'Distinct' | 'Graph':
|
|
263
|
+
case 'SelectQuery' | 'AskQuery' | 'Project' | 'Distinct' | 'Graph':
|
|
264
264
|
yield from _extract_nanopublication_uris(algebra['p'])
|
|
265
265
|
|
|
266
266
|
case 'BGP':
|
|
@@ -429,7 +429,12 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
429
429
|
|
|
430
430
|
query_result = evalQuery(self.graph, query, initBindings, base)
|
|
431
431
|
|
|
432
|
-
|
|
432
|
+
try:
|
|
433
|
+
bindings = list(query_result['bindings'])
|
|
434
|
+
except KeyError:
|
|
435
|
+
# This was probably an ASK query
|
|
436
|
+
return query_result
|
|
437
|
+
|
|
433
438
|
for row in bindings:
|
|
434
439
|
for _, maybe_iri in row.items():
|
|
435
440
|
if (
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
dcterms: https://purl.org/dc/terms/
|
|
11
11
|
xsd: https://www.w3.org/2001/XMLSchema#
|
|
12
12
|
|
|
13
|
+
iolanta:outputs:
|
|
14
|
+
"@type": "@id"
|
|
15
|
+
|
|
16
|
+
$: rdfs:label
|
|
17
|
+
|
|
13
18
|
"@included":
|
|
14
19
|
- $id: iolanta:icon
|
|
15
20
|
rdfs:label: Icon
|
|
@@ -107,3 +112,7 @@
|
|
|
107
112
|
- $id: https://iolanta.tech/qname
|
|
108
113
|
iolanta:hasDefaultFacet:
|
|
109
114
|
$id: python://iolanta.facets.qname.QNameFacet
|
|
115
|
+
|
|
116
|
+
- $id: python://iolanta.facets.textual_property_pairs_table.TextualPropertyPairsTableFacet
|
|
117
|
+
iolanta:outputs: https://iolanta.tech/cli/textual
|
|
118
|
+
$: Subjects → Objects
|
iolanta/facets/facet.py
CHANGED
|
@@ -2,13 +2,12 @@ import inspect
|
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
3
|
from functools import cached_property
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Any, Generic, Iterable,
|
|
5
|
+
from typing import Any, Generic, Iterable, Optional, TypeVar, Union
|
|
6
6
|
|
|
7
7
|
from rdflib.term import BNode, Literal, Node, URIRef
|
|
8
8
|
|
|
9
9
|
from iolanta.models import NotLiteralNode, Triple, TripleTemplate
|
|
10
|
-
from
|
|
11
|
-
from ldflex.ldflex import QueryResult, SPARQLQueryArgument
|
|
10
|
+
from iolanta.query_result import QueryResult, SPARQLQueryArgument
|
|
12
11
|
|
|
13
12
|
FacetOutput = TypeVar('FacetOutput')
|
|
14
13
|
|
|
@@ -21,16 +20,16 @@ class Facet(Generic[FacetOutput]):
|
|
|
21
20
|
iolanta: 'iolanta.Iolanta' = field(repr=False)
|
|
22
21
|
as_datatype: Optional[NotLiteralNode] = None
|
|
23
22
|
|
|
23
|
+
@property
|
|
24
|
+
def this(self) -> NotLiteralNode:
|
|
25
|
+
"""This node."""
|
|
26
|
+
return self.iri
|
|
27
|
+
|
|
24
28
|
@property
|
|
25
29
|
def stored_queries_path(self) -> Path:
|
|
26
30
|
"""Construct directory for stored queries for this facet."""
|
|
27
31
|
return Path(inspect.getfile(self.__class__)).parent / 'sparql'
|
|
28
32
|
|
|
29
|
-
@property
|
|
30
|
-
def ldflex(self) -> LDFlex:
|
|
31
|
-
"""Extract LDFLex instance."""
|
|
32
|
-
return self.iolanta.ldflex
|
|
33
|
-
|
|
34
33
|
@cached_property
|
|
35
34
|
def uriref(self) -> NotLiteralNode:
|
|
36
35
|
"""Format as URIRef."""
|
|
@@ -45,7 +44,7 @@ class Facet(Generic[FacetOutput]):
|
|
|
45
44
|
**kwargs: SPARQLQueryArgument,
|
|
46
45
|
) -> QueryResult:
|
|
47
46
|
"""SPARQL query."""
|
|
48
|
-
return self.
|
|
47
|
+
return self.iolanta.query(
|
|
49
48
|
query_text=query_text,
|
|
50
49
|
**kwargs,
|
|
51
50
|
)
|
iolanta/facets/locator.py
CHANGED
|
@@ -15,7 +15,7 @@ class FoundRow(TypedDict):
|
|
|
15
15
|
"""Facet and datatype to render an IRI."""
|
|
16
16
|
|
|
17
17
|
facet: NotLiteralNode
|
|
18
|
-
|
|
18
|
+
output_datatype: NotLiteralNode
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def reorder_rows_by_facet_preferences( # noqa: WPS214, WPS210
|
|
@@ -103,6 +103,29 @@ class FacetFinder: # noqa: WPS214
|
|
|
103
103
|
key=self.row_sorter_by_output_datatype,
|
|
104
104
|
)
|
|
105
105
|
|
|
106
|
+
def by_sparql(self) -> Iterable[FoundRow]:
|
|
107
|
+
"""Determine facet by SHACL shape of the data."""
|
|
108
|
+
if not isinstance(self.node, URIRef):
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
if self.as_datatype != URIRef('https://iolanta.tech/cli/textual'):
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
# TODO: Retrieve queries from the graph
|
|
115
|
+
# TODO: Verify datatype for which we are visualizing $this
|
|
116
|
+
query_to_facet = {
|
|
117
|
+
'ASK WHERE { ?subject $this ?object }': URIRef(
|
|
118
|
+
'python://iolanta.facets.textual_property_pairs_table'
|
|
119
|
+
'.TextualPropertyPairsTableFacet',
|
|
120
|
+
),
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for query, facet in query_to_facet.items():
|
|
124
|
+
# TODO: Verify that `query` is an ASK query
|
|
125
|
+
is_matching = self.iolanta.query(query, this=self.node)
|
|
126
|
+
if is_matching:
|
|
127
|
+
yield FoundRow(facet=facet, output_datatype=self.as_datatype)
|
|
128
|
+
|
|
106
129
|
def by_prefix(self) -> Iterable[FoundRow]:
|
|
107
130
|
"""Determine facet by URL prefix.
|
|
108
131
|
|
|
@@ -254,6 +277,7 @@ class FacetFinder: # noqa: WPS214
|
|
|
254
277
|
|
|
255
278
|
def _found_facets(self) -> Iterable[FoundRow]:
|
|
256
279
|
"""Compose a stream of all possible facet choices."""
|
|
280
|
+
yield from self.by_sparql()
|
|
257
281
|
yield from self.by_prefix()
|
|
258
282
|
yield from self.by_datatype()
|
|
259
283
|
yield from self.by_facet()
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from typing import Iterable
|
|
3
|
+
|
|
4
|
+
import funcy
|
|
5
|
+
from rdflib import Literal, Node
|
|
6
|
+
from rich.style import Style
|
|
7
|
+
from rich.text import Text
|
|
8
|
+
from textual.containers import Vertical
|
|
9
|
+
from textual.coordinate import Coordinate
|
|
10
|
+
from textual.widgets import DataTable
|
|
11
|
+
|
|
12
|
+
from iolanta import Facet
|
|
13
|
+
from iolanta.facets.page_title import PageTitle
|
|
14
|
+
from iolanta.models import NotLiteralNode
|
|
15
|
+
from iolanta.namespaces import DATATYPES
|
|
16
|
+
from iolanta.widgets.mixin import IolantaWidgetMixin
|
|
17
|
+
|
|
18
|
+
PAIRS_QUERY = """
|
|
19
|
+
SELECT ?subject ?object WHERE {
|
|
20
|
+
?subject $this ?object .
|
|
21
|
+
} ORDER BY ?subject ?object
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PairsTable(IolantaWidgetMixin, DataTable):
|
|
26
|
+
"""Render subject → object properties as a table with two columns."""
|
|
27
|
+
|
|
28
|
+
BINDINGS = [
|
|
29
|
+
('enter', 'goto', 'Goto'),
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
def __init__(self, pairs: Iterable[tuple[NotLiteralNode, Node]]):
|
|
33
|
+
"""Construct."""
|
|
34
|
+
super().__init__(show_header=False, cell_padding=1)
|
|
35
|
+
self.pairs = list(pairs)
|
|
36
|
+
|
|
37
|
+
def render_human_readable_cells(self):
|
|
38
|
+
"""Replace the cells with their human readable titles."""
|
|
39
|
+
terms_and_coordinates = sorted(
|
|
40
|
+
self.node_to_coordinates.items(),
|
|
41
|
+
key=lambda node_and_coordinates_pair: len(
|
|
42
|
+
node_and_coordinates_pair[1],
|
|
43
|
+
),
|
|
44
|
+
reverse=True,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
for term, coordinates in terms_and_coordinates:
|
|
48
|
+
title = self.iolanta.render(term, as_datatype=DATATYPES.title)
|
|
49
|
+
for coordinate in coordinates:
|
|
50
|
+
self.app.call_from_thread(
|
|
51
|
+
self.update_cell_at,
|
|
52
|
+
coordinate,
|
|
53
|
+
value=Text(title, no_wrap=False),
|
|
54
|
+
update_width=False,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
@funcy.cached_property
|
|
58
|
+
@funcy.post_processing(dict)
|
|
59
|
+
def coordinate_to_node(self):
|
|
60
|
+
"""Return a mapping of coordinates to their corresponding nodes."""
|
|
61
|
+
for row_number, (subject, object_term) in enumerate(self.pairs):
|
|
62
|
+
yield Coordinate(row_number, 0), subject
|
|
63
|
+
yield Coordinate(row_number, 1), object_term
|
|
64
|
+
|
|
65
|
+
@funcy.cached_property
|
|
66
|
+
def node_to_coordinates(self) -> defaultdict[Node, list[Coordinate]]:
|
|
67
|
+
"""Map node to coordinates where it appears."""
|
|
68
|
+
node_to_coordinate = [
|
|
69
|
+
(node, coordinate)
|
|
70
|
+
for coordinate, node in self.coordinate_to_node.items()
|
|
71
|
+
]
|
|
72
|
+
return funcy.group_values(node_to_coordinate)
|
|
73
|
+
|
|
74
|
+
def format_as_loading(self, node: Node) -> Text:
|
|
75
|
+
"""Intermediate version of a value while it is loading."""
|
|
76
|
+
if isinstance(node, Literal):
|
|
77
|
+
node_text = f'⌛ {node}'
|
|
78
|
+
else:
|
|
79
|
+
node_text = self.iolanta.node_as_qname(node)
|
|
80
|
+
node_text = f'⌛ {node_text}'
|
|
81
|
+
|
|
82
|
+
return Text(
|
|
83
|
+
node_text,
|
|
84
|
+
style=Style(dim=True),
|
|
85
|
+
no_wrap=False,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def on_mount(self):
|
|
89
|
+
"""Fill the table and start rendering."""
|
|
90
|
+
self.add_columns('Subject', 'Object')
|
|
91
|
+
self.cell_padding = 1
|
|
92
|
+
|
|
93
|
+
for subject, object_term in self.pairs:
|
|
94
|
+
self.add_row(
|
|
95
|
+
self.format_as_loading(subject),
|
|
96
|
+
self.format_as_loading(object_term),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
self.run_worker(
|
|
100
|
+
self.render_human_readable_cells,
|
|
101
|
+
thread=True,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def action_goto(self):
|
|
105
|
+
"""Navigate to the selected node."""
|
|
106
|
+
if self.cursor_coordinate:
|
|
107
|
+
node = self.coordinate_to_node.get(self.cursor_coordinate)
|
|
108
|
+
if node is not None:
|
|
109
|
+
self.app.action_goto(node)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class TextualPropertyPairsTableFacet(Facet):
|
|
113
|
+
"""Render a table of subject → object pairs for a property."""
|
|
114
|
+
|
|
115
|
+
def show(self):
|
|
116
|
+
"""Construct the table."""
|
|
117
|
+
rows = self.query(
|
|
118
|
+
PAIRS_QUERY,
|
|
119
|
+
this=self.this,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return Vertical(
|
|
123
|
+
PageTitle(
|
|
124
|
+
self.this,
|
|
125
|
+
extra='— Subjects & Objects connected by this property',
|
|
126
|
+
),
|
|
127
|
+
PairsTable(
|
|
128
|
+
[
|
|
129
|
+
(row['subject'], row['object'])
|
|
130
|
+
for row in rows
|
|
131
|
+
],
|
|
132
|
+
),
|
|
133
|
+
)
|
iolanta/iolanta.py
CHANGED
|
@@ -18,8 +18,10 @@ from typing import ( # noqa: WPS235
|
|
|
18
18
|
import funcy
|
|
19
19
|
import loguru
|
|
20
20
|
import yaml_ld
|
|
21
|
-
from
|
|
21
|
+
from pyparsing import ParseException
|
|
22
|
+
from rdflib import ConjunctiveGraph, Graph, Literal, Namespace, URIRef
|
|
22
23
|
from rdflib.namespace import NamespaceManager
|
|
24
|
+
from rdflib.plugins.sparql.processor import SPARQLResult
|
|
23
25
|
from rdflib.term import Node
|
|
24
26
|
from yaml_ld.document_loaders.content_types import ParserNotFound
|
|
25
27
|
from yaml_ld.errors import YAMLLDError
|
|
@@ -44,8 +46,13 @@ from iolanta.node_to_qname import node_to_qname
|
|
|
44
46
|
from iolanta.parse_quads import parse_quads
|
|
45
47
|
from iolanta.parsers.yaml import YAML
|
|
46
48
|
from iolanta.plugin import Plugin
|
|
49
|
+
from iolanta.query_result import (
|
|
50
|
+
QueryResult,
|
|
51
|
+
SPARQLParseException,
|
|
52
|
+
SPARQLQueryArgument,
|
|
53
|
+
format_query_bindings,
|
|
54
|
+
)
|
|
47
55
|
from iolanta.resolvers.python_import import PythonImportResolver
|
|
48
|
-
from ldflex import LDFlex
|
|
49
56
|
|
|
50
57
|
|
|
51
58
|
class LoggerProtocol(Protocol):
|
|
@@ -124,14 +131,56 @@ class Iolanta: # noqa: WPS214
|
|
|
124
131
|
for plugin_class in self.plugin_classes
|
|
125
132
|
]
|
|
126
133
|
|
|
127
|
-
|
|
128
|
-
|
|
134
|
+
def query(
|
|
135
|
+
self,
|
|
136
|
+
query_text: str,
|
|
137
|
+
**kwargs: SPARQLQueryArgument,
|
|
138
|
+
) -> QueryResult:
|
|
129
139
|
"""
|
|
130
|
-
|
|
140
|
+
Run a SPARQL `SELECT`, `CONSTRUCT`, or `ASK` query.
|
|
131
141
|
|
|
132
|
-
|
|
142
|
+
Args:
|
|
143
|
+
query_text: The SPARQL text;
|
|
144
|
+
**kwargs: bind variables in the query to values if necessary. For
|
|
145
|
+
example:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
iolanta.query(
|
|
149
|
+
'SELECT ?title WHERE { ?page rdfs:label ?title }',
|
|
150
|
+
?page=page_iri,
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Results of the query:
|
|
156
|
+
|
|
157
|
+
- a graph for `CONSTRUCT`,
|
|
158
|
+
- a list of dicts for `SELECT`,
|
|
159
|
+
- or a boolean for `ASK`.
|
|
133
160
|
"""
|
|
134
|
-
|
|
161
|
+
try:
|
|
162
|
+
sparql_result: SPARQLResult = self.graph.query(
|
|
163
|
+
query_text,
|
|
164
|
+
processor='cyberspace',
|
|
165
|
+
initBindings=kwargs,
|
|
166
|
+
)
|
|
167
|
+
except ParseException as err:
|
|
168
|
+
raise SPARQLParseException(
|
|
169
|
+
error=err,
|
|
170
|
+
query=query_text,
|
|
171
|
+
) from err
|
|
172
|
+
|
|
173
|
+
if sparql_result.askAnswer is not None:
|
|
174
|
+
return sparql_result.askAnswer
|
|
175
|
+
|
|
176
|
+
if sparql_result.graph is not None:
|
|
177
|
+
graph: Graph = sparql_result.graph
|
|
178
|
+
for prefix, namespace in self.graph.namespaces():
|
|
179
|
+
graph.bind(prefix, namespace)
|
|
180
|
+
|
|
181
|
+
return graph
|
|
182
|
+
|
|
183
|
+
return format_query_bindings(sparql_result.bindings)
|
|
135
184
|
|
|
136
185
|
@functools.cached_property
|
|
137
186
|
def namespaces_to_bind(self) -> Dict[str, Namespace]:
|
|
@@ -253,12 +302,6 @@ class Iolanta: # noqa: WPS214
|
|
|
253
302
|
self.graph.bind(prefix='dcterms', namespace=namespaces.DCTERMS)
|
|
254
303
|
self.graph.bind(prefix='rdfg', namespace=namespaces.RDFG)
|
|
255
304
|
|
|
256
|
-
@property
|
|
257
|
-
def query(self):
|
|
258
|
-
"""Make a SPARQL query."""
|
|
259
|
-
self.maybe_infer()
|
|
260
|
-
return self.ldflex.query
|
|
261
|
-
|
|
262
305
|
@functools.cached_property
|
|
263
306
|
def context_paths(self) -> Iterable[Path]:
|
|
264
307
|
"""
|
iolanta/query_result.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Dict, List, Mapping, Optional, Union
|
|
3
|
+
|
|
4
|
+
from documented import DocumentedError
|
|
5
|
+
from frozendict import frozendict
|
|
6
|
+
from pyparsing import ParseException
|
|
7
|
+
from rdflib import Graph
|
|
8
|
+
from rdflib.term import Identifier, Node, Variable
|
|
9
|
+
|
|
10
|
+
SelectRow = Mapping[str, Node]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SelectResult(List[SelectRow]):
|
|
14
|
+
"""Result of a SPARQL SELECT."""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def first(self) -> Optional[SelectRow]:
|
|
18
|
+
"""Return first element of the list."""
|
|
19
|
+
return self[0] if self else None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
SPARQLQueryArgument = Optional[Union[Node, str, int, float]]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
QueryResult = Union[
|
|
26
|
+
SelectResult, # SELECT
|
|
27
|
+
Graph, # CONSTRUCT
|
|
28
|
+
bool, # ASK
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def format_query_bindings(
|
|
33
|
+
bindings: List[Dict[Variable, Identifier]],
|
|
34
|
+
) -> SelectResult:
|
|
35
|
+
"""
|
|
36
|
+
Format bindings before returning them.
|
|
37
|
+
|
|
38
|
+
Converts Variable to str for ease of addressing.
|
|
39
|
+
"""
|
|
40
|
+
return SelectResult([
|
|
41
|
+
frozendict({
|
|
42
|
+
str(variable_name): rdf_value
|
|
43
|
+
for variable_name, rdf_value # noqa: WPS361
|
|
44
|
+
in row.items()
|
|
45
|
+
})
|
|
46
|
+
for row in bindings
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class SPARQLParseException(DocumentedError):
|
|
52
|
+
"""
|
|
53
|
+
SPARQL query is invalid.
|
|
54
|
+
|
|
55
|
+
Error:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
{self.error}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Query:
|
|
62
|
+
```sparql hl_lines="{self.highlight_code}"
|
|
63
|
+
{self.query}
|
|
64
|
+
```
|
|
65
|
+
""" # noqa: D412
|
|
66
|
+
|
|
67
|
+
error: ParseException
|
|
68
|
+
query: str
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def highlight_code(self):
|
|
72
|
+
"""Define lines to highlight."""
|
|
73
|
+
return self.error.lineno
|
|
@@ -2,10 +2,10 @@ iolanta/__init__.py,sha256=BvLP-LYFmNYS5F8VPXdzpEEMk8zcm9NMmlUWw9yEtj0,153
|
|
|
2
2
|
iolanta/base_plugin.py,sha256=vI4DRSIITlKZw8x7Q58BFkChWlg1h8zV-tuNvIKURBI,86
|
|
3
3
|
iolanta/cli/__init__.py,sha256=IV6_RPmrbpPWbdKojuFczwaJAaKm1DIDQh5aZWbeR1U,69
|
|
4
4
|
iolanta/cli/formatters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
iolanta/cli/formatters/choose.py,sha256=
|
|
6
|
-
iolanta/cli/formatters/csv.py,sha256=
|
|
7
|
-
iolanta/cli/formatters/json.py,sha256=
|
|
8
|
-
iolanta/cli/formatters/pretty.py,sha256=
|
|
5
|
+
iolanta/cli/formatters/choose.py,sha256=LWzsO_9IBSSgYNIyLlItkp8TNvpW3v6YCQ8-6kbICI4,1129
|
|
6
|
+
iolanta/cli/formatters/csv.py,sha256=ceJ_DTz0beqeK-d6FPBQqqjXrziEfF0FRSLoGZCt_fs,760
|
|
7
|
+
iolanta/cli/formatters/json.py,sha256=Og5B9UrSM_0NWqW5Afpsy6WH8ZfYgPMVXYvT3i-43Jc,748
|
|
8
|
+
iolanta/cli/formatters/pretty.py,sha256=IypZRAr2vNqcXFY6NOIc75mpyfpFWh56aCBlOPDDieQ,2901
|
|
9
9
|
iolanta/cli/main.py,sha256=exDWPljllufn7DiGC9lS1pvojFX9YZJciQ2RPCedKMU,3127
|
|
10
10
|
iolanta/cli/models.py,sha256=cjbpowdzI4wAP0DUk3qoVHyimk6AZwlXi9CGmusZTuM,159
|
|
11
11
|
iolanta/cli/pretty_print.py,sha256=M6E3TmhzA6JY5GeUVmDZLmOh5u70-393PVit4voFKDI,977
|
|
@@ -14,12 +14,12 @@ iolanta/conversions.py,sha256=hbLwRF1bAbOxy17eMWLHhYksbdCWN-v4-0y0wn3XSSg,1185
|
|
|
14
14
|
iolanta/cyberspace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
iolanta/cyberspace/inference/wikibase-claim.sparql,sha256=JSawj3VTc9ZPoU9mvh1w1AMYb3cWyZ3x1rEYWUsKZ6A,209
|
|
16
16
|
iolanta/cyberspace/inference/wikibase-statement-property.sparql,sha256=SkSHZZlxWVDwBM3aLo0Q7hLuOj9BsIQnXtcuAuwaxqU,240
|
|
17
|
-
iolanta/cyberspace/processor.py,sha256=
|
|
17
|
+
iolanta/cyberspace/processor.py,sha256=USGe54XuNMNYYl-Hed1bKxeqZLUhrb1jNRUDbWpay4A,21990
|
|
18
18
|
iolanta/data/cli.yaml,sha256=TsnldYXoY5GIzoNuPDvwBKGw8eAEForZW1FCKqKI0Kg,1029
|
|
19
19
|
iolanta/data/context.yaml,sha256=OULEeDkSqshabaXF_gMujgwFLDJvt9eQn_9FftUlSUw,1424
|
|
20
20
|
iolanta/data/html.yaml,sha256=hVFdLWLy8FMY8xpOrJMYc-tE3S0Nq83xuxVkjRW_7rI,517
|
|
21
21
|
iolanta/data/iolanta.yaml,sha256=xubIFBNU02lmFXhgOSuyQwUcZD3xCqVfeVAZMvOxKbI,1433
|
|
22
|
-
iolanta/data/textual-browser.yaml,sha256=
|
|
22
|
+
iolanta/data/textual-browser.yaml,sha256=8aGKLN_xnSX5o3dpBLsh8kPMfEsiysOd2QX-pn0Vfd4,4031
|
|
23
23
|
iolanta/ensure_is_context.py,sha256=9aok8asyEx7KPesOR28VBDb3Ch9kfc3eoCpQSJwj07U,717
|
|
24
24
|
iolanta/entry_points.py,sha256=DZbf-udlEwELFGqeWENj0M2BOUPOWlmGJdqyaEtnot0,504
|
|
25
25
|
iolanta/errors.py,sha256=t_RltahnoEvcytVa1BOq2MADgHps3JNd_h5-SFu7_wM,1250
|
|
@@ -31,7 +31,7 @@ iolanta/facets/cli/record.py,sha256=lBsECxLkwVSXC-yHmUwfosxAdLBI-0UoqHe8GOCablY,
|
|
|
31
31
|
iolanta/facets/cli/sparql/link.sparql,sha256=WtWEfLAvdGc2gP0IhZil6Vglkydc3VO24vk4GwRnR5I,163
|
|
32
32
|
iolanta/facets/cli/sparql/record.sparql,sha256=GBWkmNelvaSDbvl7v0-LsJHdjFPbsSAW49kNFOUeoGQ,63
|
|
33
33
|
iolanta/facets/errors.py,sha256=sEBmnzhFcRIgOT18hfNwyMMjry0waa6IB-jC2NVTA50,4124
|
|
34
|
-
iolanta/facets/facet.py,sha256=
|
|
34
|
+
iolanta/facets/facet.py,sha256=QByUVrvt_XVBoVhfuyk6lKO--pauhMEN79GWmgejdIo,2822
|
|
35
35
|
iolanta/facets/foaf_person_title/__init__.py,sha256=oj4MNVQvv8Dysb27xiWjtZCii8-nT7-WFa3WMWUwbtU,67
|
|
36
36
|
iolanta/facets/foaf_person_title/facet.py,sha256=Q9TajjGp1v729quDzAM_Lfzawls3yujp1Z_6jXrG3gY,632
|
|
37
37
|
iolanta/facets/foaf_person_title/sparql/names.sparql,sha256=p_2hHZXhEaJ8IwGlvLoN0vb0vhGqo44uAVRpDyTzflU,107
|
|
@@ -45,7 +45,7 @@ iolanta/facets/html/base.py,sha256=JcpK7YM_QQE9vwH5w5F_EgPkPv-XRzOrcZlVSy4LhIs,1
|
|
|
45
45
|
iolanta/facets/html/code_literal.py,sha256=qCddzBrg6Y5XMIKohFQ52Tf9GPOcU7bj83tfAU1FLLU,394
|
|
46
46
|
iolanta/facets/html/default.py,sha256=Dmf_kYiL2M954iigyYsiWrMstwZ1nvxKetuR6aW3xYY,647
|
|
47
47
|
iolanta/facets/icon.py,sha256=ddZcolx1Q5_wo3w0jqiCzcc5Lsru6-jA7s7oAd1T8Og,494
|
|
48
|
-
iolanta/facets/locator.py,sha256=
|
|
48
|
+
iolanta/facets/locator.py,sha256=0FNISVG_Q4Mk8j6ZX0tmIM-YfnewdKJSf7BYQIwb-lc,8636
|
|
49
49
|
iolanta/facets/page_title.py,sha256=TwgZK2g_e5UoWYjKNgDzzkmq1EI3cY58680iC8N9kZI,1407
|
|
50
50
|
iolanta/facets/qname.py,sha256=ztyBbjjcW8dNZzuiNMqhcWfAUxk-gSjbseVGrQE7kVY,531
|
|
51
51
|
iolanta/facets/textual_browser/__init__.py,sha256=sKgDvXOwib9n9d63kdtKCEv26-FoL0VN6zxDmfcheZ8,104
|
|
@@ -86,6 +86,7 @@ iolanta/facets/textual_ontology/__init__.py,sha256=3H6bfYaEbXFr90C6ZpGWC4O-24uaO
|
|
|
86
86
|
iolanta/facets/textual_ontology/facets.py,sha256=60g8ANmePb9_i_-d4ui-FdtNwg9aktrOKXHkTQtLp1w,3338
|
|
87
87
|
iolanta/facets/textual_ontology/sparql/terms.sparql,sha256=oVLxN452nqog_95qRaTWnvar6rxPNxPrRonSo7oFFTg,324
|
|
88
88
|
iolanta/facets/textual_ontology/sparql/visualization-vocab.sparql,sha256=q9TmU15deL0da28mpo_8W8fgMSEcENfYeqLyM0zVbTg,65
|
|
89
|
+
iolanta/facets/textual_property_pairs_table.py,sha256=Drqc_G_6QhzNmrrfDU170eKTGrVmvQ6JMYu4ir--iUk,4176
|
|
89
90
|
iolanta/facets/textual_provenance/__init__.py,sha256=k5-_iK8Lrdwr5ZEJaDxq-UhGYe4G_adXVqGfOA5DAP8,114
|
|
90
91
|
iolanta/facets/textual_provenance/facets.py,sha256=vv3UQsI2duB36DW5Zkw3sqgAXBPmK_xAo7cU0O7jF8g,3767
|
|
91
92
|
iolanta/facets/textual_provenance/sparql/graphs.sparql,sha256=B45uKFd-1vrBuMDSbTURjUUEjHt51vAbqdL4tUcgMvk,103
|
|
@@ -96,7 +97,7 @@ iolanta/facets/title/sparql/title.sparql,sha256=4rz47tjwX2OJavWMzftaYKil1-ZHB76Z
|
|
|
96
97
|
iolanta/facets/wikibase_statement_title/__init__.py,sha256=_yk1akxgSJOiUBJIc8QGrD2vovvmx_iw_vJDuv1rD7M,91
|
|
97
98
|
iolanta/facets/wikibase_statement_title/facets.py,sha256=mUH7twlAgoeX7DgLQuRBQv4ORT6GWbN-0eJ1aliSfiQ,724
|
|
98
99
|
iolanta/facets/wikibase_statement_title/sparql/statement-title.sparql,sha256=n07DQWxKqB5c3CA4kacq2HSN0R0dLgnMnLP1AxMo5YA,320
|
|
99
|
-
iolanta/iolanta.py,sha256=
|
|
100
|
+
iolanta/iolanta.py,sha256=ZZ5hBXuVci4v-36xYLeaqDjgo85kEnnYCA07eJMo9ME,14230
|
|
100
101
|
iolanta/loaders/__init__.py,sha256=QTiKCsQc1BTS-IlY2CQsN9iVpEIPqYFvI9ERMYVZCbU,99
|
|
101
102
|
iolanta/loaders/base.py,sha256=-DxYwqG1bfDXB2p_S-mKpkc_3Sh14OHhePbe65Iq3-s,3381
|
|
102
103
|
iolanta/loaders/data_type_choice.py,sha256=zRUXBIzjvuW28P_dhMDVevE9C8EFEIx2_X39WydWrtM,1982
|
|
@@ -118,6 +119,7 @@ iolanta/parsers/json.py,sha256=3VrDiz-SaVXP9_B03Gl62VSIk2fKeRrrb2hpTLxqBk4,967
|
|
|
118
119
|
iolanta/parsers/markdown.py,sha256=wv-PhszgoDOzWdisykIeZyYBD1edCVmw7UQ8jEn4siw,1669
|
|
119
120
|
iolanta/parsers/yaml.py,sha256=MeTe5_OaDK27XB38AzxjqWfCxybAm4tqgqn7LLh-YB0,1244
|
|
120
121
|
iolanta/plugin.py,sha256=MSxpuOIx93AgBahfS8bYh31MEgcwtUSQhj4Js7fgdSI,1096
|
|
122
|
+
iolanta/query_result.py,sha256=VLLBkewUEymtzfB0jeIeRE3Np6pAgo959RPgNsEmiq8,1545
|
|
121
123
|
iolanta/reformat_blank_nodes.py,sha256=MAVcXusUioKzAoTEHAMume5Gt9vBEpxJGrngqFzmkJI,712
|
|
122
124
|
iolanta/resolvers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
123
125
|
iolanta/resolvers/base.py,sha256=cc9bcrVZ0wTwn85I-IYCwcIRU5Lwaph8D00C0dwSOm8,302
|
|
@@ -125,9 +127,7 @@ iolanta/resolvers/python_import.py,sha256=kDOhApBDFfxnrDgK9M7ztMkqXfcny81Zo86FgP
|
|
|
125
127
|
iolanta/shortcuts.py,sha256=j8b0E_yeoas8GumsPOfxO2v2jO0noqpmKJ6LFxFfsu4,1892
|
|
126
128
|
iolanta/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
127
129
|
iolanta/widgets/mixin.py,sha256=nDRCOc-gizCf1a5DAcYs4hW8eZEd6pHBPFsfm0ncv7E,251
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
iolanta-2.0.
|
|
131
|
-
iolanta-2.0.
|
|
132
|
-
iolanta-2.0.0.dist-info/entry_points.txt,sha256=fIp9g4kzjSNcTsTSjUCk4BIG3laHd3b3hUZlkjgFAGU,179
|
|
133
|
-
iolanta-2.0.0.dist-info/RECORD,,
|
|
130
|
+
iolanta-2.0.2.dist-info/METADATA,sha256=BMCrSsOvKpNz7vzyS1iFXXWffpee_tGU1Rtvf_BF4Xc,2230
|
|
131
|
+
iolanta-2.0.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
132
|
+
iolanta-2.0.2.dist-info/entry_points.txt,sha256=fIp9g4kzjSNcTsTSjUCk4BIG3laHd3b3hUZlkjgFAGU,179
|
|
133
|
+
iolanta-2.0.2.dist-info/RECORD,,
|
ldflex/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from ldflex.ldflex import LDFlex
|
ldflex/ldflex.py
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import Dict, List, Mapping, Optional, Union
|
|
3
|
-
|
|
4
|
-
from documented import DocumentedError
|
|
5
|
-
from frozendict import frozendict
|
|
6
|
-
from pyparsing import ParseException
|
|
7
|
-
from rdflib import Graph
|
|
8
|
-
from rdflib.plugins.sparql.processor import SPARQLResult
|
|
9
|
-
from rdflib.term import Identifier, Node, Variable
|
|
10
|
-
|
|
11
|
-
from iolanta.cyberspace.processor import GlobalSPARQLProcessor
|
|
12
|
-
|
|
13
|
-
SelectRow = Mapping[str, Node]
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class SelectResult(List[SelectRow]):
|
|
17
|
-
"""Result of a SPARQL SELECT."""
|
|
18
|
-
|
|
19
|
-
@property
|
|
20
|
-
def first(self) -> Optional[SelectRow]:
|
|
21
|
-
"""Return first element of the list."""
|
|
22
|
-
return self[0] if self else None
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
SPARQLQueryArgument = Optional[Union[Node, str, int, float]]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
QueryResult = Union[
|
|
29
|
-
SelectResult, # SELECT
|
|
30
|
-
Graph, # CONSTRUCT
|
|
31
|
-
bool, # ASK
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def _format_query_bindings(
|
|
36
|
-
bindings: List[Dict[Variable, Identifier]],
|
|
37
|
-
) -> SelectResult:
|
|
38
|
-
"""
|
|
39
|
-
Format bindings before returning them.
|
|
40
|
-
|
|
41
|
-
Converts Variable to str for ease of addressing.
|
|
42
|
-
"""
|
|
43
|
-
return SelectResult([
|
|
44
|
-
frozendict({
|
|
45
|
-
str(variable_name): rdf_value
|
|
46
|
-
for variable_name, rdf_value
|
|
47
|
-
in row.items()
|
|
48
|
-
})
|
|
49
|
-
for row in bindings
|
|
50
|
-
])
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@dataclass
|
|
54
|
-
class SPARQLParseException(DocumentedError):
|
|
55
|
-
"""
|
|
56
|
-
SPARQL query is invalid.
|
|
57
|
-
|
|
58
|
-
Error:
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
{self.error}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Query:
|
|
65
|
-
```sparql hl_lines="{self.highlight_code}"
|
|
66
|
-
{self.query}
|
|
67
|
-
```
|
|
68
|
-
"""
|
|
69
|
-
|
|
70
|
-
error: ParseException
|
|
71
|
-
query: str
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
def highlight_code(self):
|
|
75
|
-
"""Define lines to highlight."""
|
|
76
|
-
return self.error.lineno
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@dataclass
|
|
80
|
-
class LDFlex:
|
|
81
|
-
"""Fluent interface to a semantic graph."""
|
|
82
|
-
|
|
83
|
-
graph: Graph
|
|
84
|
-
|
|
85
|
-
def query(
|
|
86
|
-
self,
|
|
87
|
-
query_text: str,
|
|
88
|
-
**kwargs: SPARQLQueryArgument,
|
|
89
|
-
) -> QueryResult:
|
|
90
|
-
"""
|
|
91
|
-
Run a SPARQL `SELECT`, `CONSTRUCT`, or `ASK` query.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
query_text: The SPARQL text;
|
|
95
|
-
**kwargs: bind variables in the query to values if necessary. For
|
|
96
|
-
example:
|
|
97
|
-
|
|
98
|
-
```python
|
|
99
|
-
ldflex.query(
|
|
100
|
-
'SELECT ?title WHERE { ?page rdfs:label ?title }',
|
|
101
|
-
?page=page_iri,
|
|
102
|
-
)
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Returns:
|
|
106
|
-
Results of the query:
|
|
107
|
-
|
|
108
|
-
- a graph for `CONSTRUCT`,
|
|
109
|
-
- a list of dicts for `SELECT`,
|
|
110
|
-
- or a boolean for `ASK`.
|
|
111
|
-
"""
|
|
112
|
-
try:
|
|
113
|
-
sparql_result: SPARQLResult = self.graph.query(
|
|
114
|
-
query_text,
|
|
115
|
-
processor='cyberspace',
|
|
116
|
-
initBindings=kwargs,
|
|
117
|
-
)
|
|
118
|
-
except ParseException as err:
|
|
119
|
-
raise SPARQLParseException(
|
|
120
|
-
error=err,
|
|
121
|
-
query=query_text,
|
|
122
|
-
) from err
|
|
123
|
-
|
|
124
|
-
if sparql_result.askAnswer is not None:
|
|
125
|
-
return sparql_result.askAnswer
|
|
126
|
-
|
|
127
|
-
if sparql_result.graph is not None:
|
|
128
|
-
graph: Graph = sparql_result.graph
|
|
129
|
-
for prefix, namespace in self.graph.namespaces():
|
|
130
|
-
graph.bind(prefix, namespace)
|
|
131
|
-
|
|
132
|
-
return graph
|
|
133
|
-
|
|
134
|
-
return _format_query_bindings(sparql_result.bindings)
|
|
135
|
-
|
|
136
|
-
def update(self, sparql_query: str):
|
|
137
|
-
"""Apply the given SPARQL INSERT query."""
|
|
138
|
-
self.graph.update(sparql_query)
|
|
File without changes
|
|
File without changes
|