iolanta 1.2.8__py3-none-any.whl → 1.2.10__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 +78 -25
- iolanta/facets/page_title.py +1 -2
- iolanta/facets/textual_browser/page_switcher.py +8 -1
- {iolanta-1.2.8.dist-info → iolanta-1.2.10.dist-info}/METADATA +30 -4
- {iolanta-1.2.8.dist-info → iolanta-1.2.10.dist-info}/RECORD +7 -7
- {iolanta-1.2.8.dist-info → iolanta-1.2.10.dist-info}/WHEEL +0 -0
- {iolanta-1.2.8.dist-info → iolanta-1.2.10.dist-info}/entry_points.txt +0 -0
iolanta/cyberspace/processor.py
CHANGED
|
@@ -50,6 +50,9 @@ NORMALIZE_TERMS_MAP = MappingProxyType({
|
|
|
50
50
|
|
|
51
51
|
REASONING_ENABLED = True
|
|
52
52
|
OWL_REASONING_ENABLED = False
|
|
53
|
+
INDICES = [
|
|
54
|
+
URIRef('https://iolanta.tech/visualizations/index.yaml'),
|
|
55
|
+
]
|
|
53
56
|
|
|
54
57
|
|
|
55
58
|
REDIRECTS = MappingProxyType({
|
|
@@ -67,7 +70,10 @@ REDIRECTS = MappingProxyType({
|
|
|
67
70
|
# This one does not answer via HTTPS :(
|
|
68
71
|
URIRef('https://xmlns.com/foaf/0.1/'): URIRef('http://xmlns.com/foaf/0.1/'),
|
|
69
72
|
URIRef('https://www.nanopub.org/nschema'): URIRef(
|
|
70
|
-
'https://www.nanopub.net/nschema',
|
|
73
|
+
'https://www.nanopub.net/nschema#',
|
|
74
|
+
),
|
|
75
|
+
URIRef('https://nanopub.org/nschema'): URIRef(
|
|
76
|
+
'https://nanopub.net/nschema#',
|
|
71
77
|
),
|
|
72
78
|
URIRef(PROV): URIRef('https://www.w3.org/ns/prov-o'),
|
|
73
79
|
})
|
|
@@ -278,6 +284,15 @@ def _extract_nanopublication_uris(
|
|
|
278
284
|
)
|
|
279
285
|
|
|
280
286
|
|
|
287
|
+
def apply_redirect(source: URIRef) -> URIRef:
|
|
288
|
+
"""Rewrite the URL."""
|
|
289
|
+
for pattern, destination in REDIRECTS.items():
|
|
290
|
+
if source.startswith(pattern):
|
|
291
|
+
return destination
|
|
292
|
+
|
|
293
|
+
return source
|
|
294
|
+
|
|
295
|
+
|
|
281
296
|
@dataclasses.dataclass(frozen=True)
|
|
282
297
|
class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
283
298
|
"""
|
|
@@ -294,6 +309,7 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
294
309
|
def __post_init__(self):
|
|
295
310
|
"""Note that we do not presently need OWL inference."""
|
|
296
311
|
self.graph.last_not_inferred_source = None
|
|
312
|
+
self.graph._indices_loaded = False
|
|
297
313
|
|
|
298
314
|
def _infer_with_sparql(self):
|
|
299
315
|
"""
|
|
@@ -353,17 +369,12 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
353
369
|
]
|
|
354
370
|
self.graph.addN(inferred_quads)
|
|
355
371
|
|
|
356
|
-
def
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
self.
|
|
360
|
-
'Rewriting: {source} → {destination}',
|
|
361
|
-
source=source,
|
|
362
|
-
destination=destination,
|
|
363
|
-
)
|
|
364
|
-
return destination
|
|
372
|
+
def _maybe_load_indices(self):
|
|
373
|
+
if not self.graph._indices_loaded:
|
|
374
|
+
for index in INDICES:
|
|
375
|
+
self.load(index)
|
|
365
376
|
|
|
366
|
-
|
|
377
|
+
self.graph._indices_loaded = True
|
|
367
378
|
|
|
368
379
|
def query( # noqa: WPS211, WPS210, WPS231, C901
|
|
369
380
|
self,
|
|
@@ -378,6 +389,8 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
378
389
|
namespaces. The given base is used to resolve relative URIs in
|
|
379
390
|
the query and will be overridden by any BASE given in the query.
|
|
380
391
|
"""
|
|
392
|
+
self._maybe_load_indices()
|
|
393
|
+
|
|
381
394
|
initBindings = initBindings or {}
|
|
382
395
|
initNs = initNs or {}
|
|
383
396
|
|
|
@@ -406,7 +419,7 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
406
419
|
try:
|
|
407
420
|
self.load(url)
|
|
408
421
|
except Exception as err:
|
|
409
|
-
self.logger.error('Failed to load
|
|
422
|
+
self.logger.error(f'Failed to load {url}: {err}', url, err)
|
|
410
423
|
|
|
411
424
|
self.maybe_apply_inference()
|
|
412
425
|
|
|
@@ -432,6 +445,34 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
432
445
|
query_result['bindings'] = bindings
|
|
433
446
|
return query_result
|
|
434
447
|
|
|
448
|
+
def _is_loaded(self, uri: URIRef) -> bool:
|
|
449
|
+
"""Find out if this URI in the graph already."""
|
|
450
|
+
return funcy.first(
|
|
451
|
+
self.graph.quads((
|
|
452
|
+
uri,
|
|
453
|
+
IOLANTA['last-loaded-time'],
|
|
454
|
+
None,
|
|
455
|
+
URIRef('iolanta://_meta'),
|
|
456
|
+
)),
|
|
457
|
+
) is not None
|
|
458
|
+
|
|
459
|
+
def _mark_as_loaded(self, uri: URIRef):
|
|
460
|
+
self.graph.add((
|
|
461
|
+
uri,
|
|
462
|
+
IOLANTA['last-loaded-time'],
|
|
463
|
+
Literal(datetime.datetime.now()),
|
|
464
|
+
URIRef('iolanta://_meta'),
|
|
465
|
+
))
|
|
466
|
+
|
|
467
|
+
def _follow_is_visualized_with_links(self, uri: URIRef):
|
|
468
|
+
"""Follow `iolanta:is-visualized-with` links."""
|
|
469
|
+
self.logger.info(f'Following links for {uri}…')
|
|
470
|
+
triples = self.graph.triples((uri, IOLANTA['visualized-with'], None))
|
|
471
|
+
for _, _, visualization in triples:
|
|
472
|
+
if isinstance(visualization, URIRef):
|
|
473
|
+
self.load(visualization)
|
|
474
|
+
self.logger.info('Links followed.')
|
|
475
|
+
|
|
435
476
|
def load( # noqa: C901, WPS210, WPS212, WPS213, WPS231
|
|
436
477
|
self,
|
|
437
478
|
source: URIRef,
|
|
@@ -448,25 +489,30 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
448
489
|
# fails.
|
|
449
490
|
return Skipped()
|
|
450
491
|
|
|
451
|
-
|
|
492
|
+
if url.fragment:
|
|
493
|
+
# Fragment on an HTML page resolves to that same page. Let us remove
|
|
494
|
+
# this ambiguity, then.
|
|
495
|
+
# TODO: It works differently for JSON-LD documents AFAIK. Need to
|
|
496
|
+
# double check that.
|
|
497
|
+
url = url.with_fragment(None)
|
|
498
|
+
source = URIRef(str(f'{url}#'))
|
|
499
|
+
|
|
500
|
+
self._follow_is_visualized_with_links(source)
|
|
501
|
+
|
|
502
|
+
new_source = apply_redirect(source)
|
|
452
503
|
if new_source != source:
|
|
504
|
+
self.logger.info(
|
|
505
|
+
'Rewriting: {source} → {new_source}',
|
|
506
|
+
source=source,
|
|
507
|
+
new_source=new_source,
|
|
508
|
+
)
|
|
453
509
|
return self.load(new_source)
|
|
454
510
|
|
|
455
511
|
source_uri = normalize_term(source)
|
|
456
|
-
|
|
457
|
-
self.graph.quads(
|
|
458
|
-
(
|
|
459
|
-
None,
|
|
460
|
-
None,
|
|
461
|
-
None,
|
|
462
|
-
source_uri,
|
|
463
|
-
),
|
|
464
|
-
),
|
|
465
|
-
)
|
|
466
|
-
if existing_triple is not None:
|
|
512
|
+
if self._is_loaded(source_uri):
|
|
467
513
|
return Skipped()
|
|
468
514
|
else:
|
|
469
|
-
self.logger.info(f'
|
|
515
|
+
self.logger.info(f'{source_uri} is not loaded yet')
|
|
470
516
|
|
|
471
517
|
# FIXME This is definitely inefficient. However, python-yaml-ld caches
|
|
472
518
|
# the document, so the performance overhead is not super high.
|
|
@@ -510,6 +556,8 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
510
556
|
RDFS.Class,
|
|
511
557
|
))
|
|
512
558
|
|
|
559
|
+
self._mark_as_loaded(source_uri)
|
|
560
|
+
|
|
513
561
|
return Loaded()
|
|
514
562
|
|
|
515
563
|
except Exception as err:
|
|
@@ -533,6 +581,9 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
533
581
|
RDF.type,
|
|
534
582
|
RDFS.Class,
|
|
535
583
|
))
|
|
584
|
+
|
|
585
|
+
self._mark_as_loaded(source_uri)
|
|
586
|
+
|
|
536
587
|
return Loaded()
|
|
537
588
|
|
|
538
589
|
if _resolved_source:
|
|
@@ -557,6 +608,8 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
|
|
|
557
608
|
RDFS.Class,
|
|
558
609
|
))
|
|
559
610
|
|
|
611
|
+
self._mark_as_loaded(source_uri)
|
|
612
|
+
|
|
560
613
|
try: # noqa: WPS225
|
|
561
614
|
ld_rdf = yaml_ld.to_rdf(source)
|
|
562
615
|
except ConnectionError as name_resolution_error:
|
iolanta/facets/page_title.py
CHANGED
|
@@ -258,6 +258,10 @@ class PageSwitcher(IolantaWidgetMixin, ContentSwitcher): # noqa: WPS214
|
|
|
258
258
|
return not is_loading
|
|
259
259
|
case 'abort':
|
|
260
260
|
return is_loading
|
|
261
|
+
case 'back':
|
|
262
|
+
return bool(self.history.past)
|
|
263
|
+
case 'forward':
|
|
264
|
+
return bool(self.history.future)
|
|
261
265
|
|
|
262
266
|
return True
|
|
263
267
|
|
|
@@ -288,12 +292,15 @@ class PageSwitcher(IolantaWidgetMixin, ContentSwitcher): # noqa: WPS214
|
|
|
288
292
|
def action_back(self):
|
|
289
293
|
"""Go backward."""
|
|
290
294
|
self.current = self.history.back().page_id
|
|
291
|
-
self.
|
|
295
|
+
if page := self.visible_content:
|
|
296
|
+
page.focus()
|
|
292
297
|
|
|
293
298
|
def action_forward(self):
|
|
294
299
|
"""Go forward."""
|
|
295
300
|
self.current = self.history.forward().page_id
|
|
296
301
|
self.focus()
|
|
302
|
+
if page := self.visible_content:
|
|
303
|
+
page.focus()
|
|
297
304
|
|
|
298
305
|
|
|
299
306
|
class ConsoleSwitcher(ContentSwitcher):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: iolanta
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.10
|
|
4
4
|
Summary: Semantic Web browser
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Anatoly Scherbakov
|
|
@@ -33,12 +33,38 @@ Requires-Dist: rich (>=13.3.1)
|
|
|
33
33
|
Requires-Dist: textual (>=0.83.0)
|
|
34
34
|
Requires-Dist: typer (>=0.9.0)
|
|
35
35
|
Requires-Dist: watchfiles (>=1.0.4,<2.0.0)
|
|
36
|
-
Requires-Dist: yaml-ld (>=1.1.
|
|
36
|
+
Requires-Dist: yaml-ld (>=1.1.5)
|
|
37
37
|
Requires-Dist: yarl (>=1.9.4)
|
|
38
38
|
Description-Content-Type: text/markdown
|
|
39
39
|
|
|
40
|
-
# iolanta
|
|
40
|
+
# iolanta | Linked Data browser
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
---
|
|
43
43
|
|
|
44
|
+
[](https://github.com/iolanta-tech/iolanta/actions?query=workflow%3Atest)
|
|
45
|
+
[](https://codecov.io/gh/iolanta-tech/iolanta)
|
|
46
|
+
[](https://pypi.org/project/iolanta/)
|
|
47
|
+
[](https://github.com/wemake-services/wemake-python-styleguide)
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
## 📦 Install with `pip`
|
|
52
|
+
|
|
53
|
+
```shell
|
|
54
|
+
pip install iolanta
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Try it out!
|
|
58
|
+
|
|
59
|
+
Explore a Nanopublication:
|
|
60
|
+
|
|
61
|
+
```shell
|
|
62
|
+
iolanta https://w3id.org/np/RA7OYmnx-3ln_AY233lElN01wSDJWDOXPz061Ah93EQ2I
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
⇒
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
See more [in the docs](https://iolanta.tech).
|
|
44
70
|
|
|
@@ -14,7 +14,7 @@ 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=QlQM93JxXU33IqGpe2jTQfpxDpu12cRVHxBrnhjjRPQ,21848
|
|
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
|
|
@@ -46,7 +46,7 @@ iolanta/facets/html/code_literal.py,sha256=qCddzBrg6Y5XMIKohFQ52Tf9GPOcU7bj83tfA
|
|
|
46
46
|
iolanta/facets/html/default.py,sha256=Dmf_kYiL2M954iigyYsiWrMstwZ1nvxKetuR6aW3xYY,647
|
|
47
47
|
iolanta/facets/icon.py,sha256=ddZcolx1Q5_wo3w0jqiCzcc5Lsru6-jA7s7oAd1T8Og,494
|
|
48
48
|
iolanta/facets/locator.py,sha256=tFwxGT4ujwEjwkgTevK6gwWfj3_1lU9Q305IU7a-I3Y,7697
|
|
49
|
-
iolanta/facets/page_title.py,sha256=
|
|
49
|
+
iolanta/facets/page_title.py,sha256=CI2V8f7zSSrwbQginhyycZttNoAZTHq34SiO8Ql59bQ,1410
|
|
50
50
|
iolanta/facets/qname.py,sha256=ztyBbjjcW8dNZzuiNMqhcWfAUxk-gSjbseVGrQE7kVY,531
|
|
51
51
|
iolanta/facets/textual_browser/__init__.py,sha256=sKgDvXOwib9n9d63kdtKCEv26-FoL0VN6zxDmfcheZ8,104
|
|
52
52
|
iolanta/facets/textual_browser/app.py,sha256=6-cmEkwRqj9UptVvXKVNIxT3PcDDOZP1OLpv8gPmB_Q,3202
|
|
@@ -56,7 +56,7 @@ iolanta/facets/textual_browser/home.py,sha256=GfDD1G2HiwdLkPkNdcYRqVIxDl5tWH9few
|
|
|
56
56
|
iolanta/facets/textual_browser/location.py,sha256=w0La8bVTpJiVo1_hFTDQeIUdDdqfhYnoihuZW-f130c,205
|
|
57
57
|
iolanta/facets/textual_browser/models.py,sha256=DqTBjhkkTt5mNwqr4DzNbPSqzV-QtNqfKj7wpn6T3ao,173
|
|
58
58
|
iolanta/facets/textual_browser/page.py,sha256=NkcQ5rSKZRbp63C8ozgsR_iVhcKHGv_SytUCQyGa7ss,786
|
|
59
|
-
iolanta/facets/textual_browser/page_switcher.py,sha256=
|
|
59
|
+
iolanta/facets/textual_browser/page_switcher.py,sha256=1-mWI56n2gLDThPBjfqHLun3utkUYdOkKSzejCIziEk,9953
|
|
60
60
|
iolanta/facets/textual_class/__init__.py,sha256=tiL0p-3JspGcBRj4qa3rmoBFAuadk71l2ja2lJN6CEs,75
|
|
61
61
|
iolanta/facets/textual_class/facets.py,sha256=W3-N7bekk-bKwrb2rTPwmySGF42-uV7aqLPAiHwFSU4,6496
|
|
62
62
|
iolanta/facets/textual_class/sparql/instances.sparql,sha256=tmG4tuYrROKo3A7idjOEblAeNPXiiExHxc6k3fhalSM,113
|
|
@@ -128,7 +128,7 @@ iolanta/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
128
128
|
iolanta/widgets/mixin.py,sha256=nDRCOc-gizCf1a5DAcYs4hW8eZEd6pHBPFsfm0ncv7E,251
|
|
129
129
|
ldflex/__init__.py,sha256=8IELqR55CQXuI0BafjobXSK7_kOc-qKVTrEtEwXnZRA,33
|
|
130
130
|
ldflex/ldflex.py,sha256=omKmOo5PUyn8Evw4q_lqKCJOq6yqVOcLAYxnYkdwOUs,3332
|
|
131
|
-
iolanta-1.2.
|
|
132
|
-
iolanta-1.2.
|
|
133
|
-
iolanta-1.2.
|
|
134
|
-
iolanta-1.2.
|
|
131
|
+
iolanta-1.2.10.dist-info/METADATA,sha256=lIe4h7C7wwGdKHlxNuq07vOGeT3M5-AXhiVUflo0KWw,2295
|
|
132
|
+
iolanta-1.2.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
133
|
+
iolanta-1.2.10.dist-info/entry_points.txt,sha256=fIp9g4kzjSNcTsTSjUCk4BIG3laHd3b3hUZlkjgFAGU,179
|
|
134
|
+
iolanta-1.2.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|