iolanta 2.0.1__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/cyberspace/processor.py +8 -3
- iolanta/data/textual-browser.yaml +9 -0
- iolanta/facets/facet.py +5 -0
- iolanta/facets/locator.py +25 -1
- iolanta/facets/textual_property_pairs_table.py +133 -0
- {iolanta-2.0.1.dist-info → iolanta-2.0.2.dist-info}/METADATA +1 -1
- {iolanta-2.0.1.dist-info → iolanta-2.0.2.dist-info}/RECORD +9 -8
- {iolanta-2.0.1.dist-info → iolanta-2.0.2.dist-info}/WHEEL +0 -0
- {iolanta-2.0.1.dist-info → iolanta-2.0.2.dist-info}/entry_points.txt +0 -0
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
|
@@ -20,6 +20,11 @@ class Facet(Generic[FacetOutput]):
|
|
|
20
20
|
iolanta: 'iolanta.Iolanta' = field(repr=False)
|
|
21
21
|
as_datatype: Optional[NotLiteralNode] = None
|
|
22
22
|
|
|
23
|
+
@property
|
|
24
|
+
def this(self) -> NotLiteralNode:
|
|
25
|
+
"""This node."""
|
|
26
|
+
return self.iri
|
|
27
|
+
|
|
23
28
|
@property
|
|
24
29
|
def stored_queries_path(self) -> Path:
|
|
25
30
|
"""Construct directory for stored queries for this facet."""
|
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
|
+
)
|
|
@@ -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
|
|
@@ -126,7 +127,7 @@ iolanta/resolvers/python_import.py,sha256=kDOhApBDFfxnrDgK9M7ztMkqXfcny81Zo86FgP
|
|
|
126
127
|
iolanta/shortcuts.py,sha256=j8b0E_yeoas8GumsPOfxO2v2jO0noqpmKJ6LFxFfsu4,1892
|
|
127
128
|
iolanta/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
128
129
|
iolanta/widgets/mixin.py,sha256=nDRCOc-gizCf1a5DAcYs4hW8eZEd6pHBPFsfm0ncv7E,251
|
|
129
|
-
iolanta-2.0.
|
|
130
|
-
iolanta-2.0.
|
|
131
|
-
iolanta-2.0.
|
|
132
|
-
iolanta-2.0.
|
|
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,,
|
|
File without changes
|
|
File without changes
|