iolanta 2.0.8__py3-none-any.whl → 2.1.1__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/main.py +1 -1
- iolanta/data/graph-triples.yamlld +2 -2
- iolanta/data/textual-browser.yaml +23 -22
- iolanta/facets/textual_browser/page_switcher.py +1 -1
- iolanta/facets/title/facets.py +2 -1
- iolanta/facets/title/sparql/title.sparql +5 -0
- iolanta/iolanta.py +33 -66
- iolanta/mermaid/__init__.py +0 -0
- iolanta/mermaid/facet.py +127 -0
- iolanta/mermaid/mermaid.yamlld +42 -0
- iolanta/mermaid/models.py +129 -0
- iolanta/mermaid/sparql/graph.sparql +5 -0
- iolanta/mermaid/sparql/subgraphs.sparql +3 -0
- iolanta/namespaces.py +1 -1
- iolanta/resolvers/base.py +2 -1
- iolanta/resolvers/dispatch.py +41 -0
- iolanta/resolvers/pypi.py +106 -0
- iolanta/resolvers/python_import.py +9 -32
- {iolanta-2.0.8.dist-info → iolanta-2.1.1.dist-info}/METADATA +3 -2
- {iolanta-2.0.8.dist-info → iolanta-2.1.1.dist-info}/RECORD +22 -16
- iolanta-2.1.1.dist-info/entry_points.txt +31 -0
- iolanta/data/cli.yaml +0 -30
- iolanta/data/html.yaml +0 -15
- iolanta-2.0.8.dist-info/entry_points.txt +0 -10
- {iolanta-2.0.8.dist-info → iolanta-2.1.1.dist-info}/WHEEL +0 -0
iolanta/cli/main.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"@context": context.yaml
|
|
2
|
-
$id:
|
|
2
|
+
$id: pkg:pypi/iolanta#textual-graph
|
|
3
3
|
$: Graph Triples
|
|
4
4
|
→: https://iolanta.tech/cli/textual
|
|
5
|
-
⪯:
|
|
5
|
+
⪯: pkg:pypi/iolanta#textual-inverse-properties
|
|
6
6
|
↦: ASK WHERE { GRAPH $this { ?s ?p ?o } }
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"@id": iolanta:is-preferred-over
|
|
31
31
|
|
|
32
32
|
↦: iolanta:matches
|
|
33
|
+
iolanta:hasDefaultFacet:
|
|
34
|
+
"@type": "@id"
|
|
33
35
|
|
|
34
36
|
"@included":
|
|
35
37
|
- $id: iolanta:icon
|
|
@@ -42,26 +44,25 @@
|
|
|
42
44
|
- $id: https://iolanta.tech/datatypes/icon
|
|
43
45
|
$: Icon
|
|
44
46
|
iolanta:hasDefaultFacet:
|
|
45
|
-
$id:
|
|
47
|
+
$id: pkg:pypi/iolanta#icon
|
|
46
48
|
→: https://iolanta.tech/datatypes/icon
|
|
47
49
|
|
|
48
50
|
- $id: np:Nanopublication
|
|
49
51
|
$: Nanopublication
|
|
50
52
|
iolanta:hasInstanceFacet:
|
|
51
|
-
$id:
|
|
53
|
+
$id: pkg:pypi/iolanta#textual-nanopublication
|
|
52
54
|
$: Nanopublication
|
|
53
55
|
→: https://iolanta.tech/cli/textual
|
|
54
56
|
⪯:
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
+
- pkg:pypi/iolanta#textual-properties
|
|
58
|
+
- pkg:pypi/iolanta#textual-graph
|
|
57
59
|
|
|
58
60
|
- $id: https://iolanta.tech/cli/interactive
|
|
59
|
-
iolanta:hasDefaultFacet:
|
|
60
|
-
$id: python://iolanta.facets.textual_browser.TextualBrowserFacet
|
|
61
|
+
iolanta:hasDefaultFacet: pkg:pypi/iolanta#textual-browser
|
|
61
62
|
|
|
62
|
-
- $id:
|
|
63
|
+
- $id: pkg:pypi/iolanta#textual-properties
|
|
63
64
|
$: Properties
|
|
64
|
-
⪯:
|
|
65
|
+
⪯: pkg:pypi/iolanta#textual-inverse-properties
|
|
65
66
|
→: https://iolanta.tech/cli/textual
|
|
66
67
|
↦: |
|
|
67
68
|
ASK WHERE {
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
FILTER (?graph != <iolanta://_meta>)
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
- $id:
|
|
73
|
+
- $id: pkg:pypi/iolanta#textual-inverse-properties
|
|
73
74
|
$: Inverse Properties
|
|
74
75
|
→: https://iolanta.tech/cli/textual
|
|
75
76
|
↦: |
|
|
@@ -80,54 +81,54 @@
|
|
|
80
81
|
|
|
81
82
|
- $id: "urn:"
|
|
82
83
|
iolanta:hasFacetByPrefix:
|
|
83
|
-
$id:
|
|
84
|
+
$id: pkg:pypi/iolanta#textual-provenance
|
|
84
85
|
→: https://iolanta.tech/cli/textual
|
|
85
86
|
|
|
86
87
|
- $id: https://iolanta.tech/cli/link
|
|
87
88
|
iolanta:hasDefaultFacet:
|
|
88
|
-
$id:
|
|
89
|
+
$id: pkg:pypi/iolanta#textual-link
|
|
89
90
|
|
|
90
91
|
- $id: https://wikiba.se/ontology#Statement
|
|
91
92
|
iolanta:hasInstanceFacet:
|
|
92
|
-
$id:
|
|
93
|
+
$id: pkg:pypi/iolanta#wikibase-statement-title
|
|
93
94
|
$: Title
|
|
94
95
|
→: https://iolanta.tech/datatypes/title
|
|
95
96
|
|
|
96
97
|
- $id: rdfs:Class
|
|
97
98
|
iolanta:hasInstanceFacet:
|
|
98
|
-
$id:
|
|
99
|
+
$id: pkg:pypi/iolanta#textual-class
|
|
99
100
|
$: Instances
|
|
100
101
|
→: https://iolanta.tech/cli/textual
|
|
101
102
|
⪯:
|
|
102
|
-
-
|
|
103
|
-
-
|
|
103
|
+
- pkg:pypi/iolanta#textual-browser
|
|
104
|
+
- pkg:pypi/iolanta#textual-inverse-properties
|
|
104
105
|
|
|
105
106
|
- $id: owl:Ontology
|
|
106
107
|
iolanta:hasInstanceFacet:
|
|
107
|
-
$id:
|
|
108
|
+
$id: pkg:pypi/iolanta#textual-ontology
|
|
108
109
|
$: Ontology
|
|
109
110
|
→: https://iolanta.tech/cli/textual
|
|
110
|
-
⪯:
|
|
111
|
+
⪯: pkg:pypi/iolanta#textual-properties
|
|
111
112
|
|
|
112
113
|
- $id: https://iolanta.tech/datatypes/textual-graph-triples
|
|
113
114
|
$: Graph Triples
|
|
114
115
|
iolanta:hasDefaultFacet:
|
|
115
|
-
$id:
|
|
116
|
+
$id: pkg:pypi/iolanta#textual-graph-triples
|
|
116
117
|
→: https://iolanta.tech/datatypes/textual-graph-triples
|
|
117
118
|
|
|
118
119
|
- $id: https://iolanta.tech/qname
|
|
119
120
|
iolanta:hasDefaultFacet:
|
|
120
|
-
$id:
|
|
121
|
+
$id: pkg:pypi/iolanta#qname
|
|
121
122
|
|
|
122
|
-
- $id:
|
|
123
|
+
- $id: pkg:pypi/iolanta#textual-property-pairs
|
|
123
124
|
$: Subjects → Objects
|
|
124
125
|
→: https://iolanta.tech/cli/textual
|
|
125
126
|
↦: ASK WHERE { ?subject $this ?object }
|
|
126
127
|
|
|
127
|
-
- $id:
|
|
128
|
+
- $id: pkg:pypi/iolanta#textual-class
|
|
128
129
|
↦:
|
|
129
130
|
- ASK WHERE { ?instance a $this }
|
|
130
131
|
- ASK WHERE { $this rdfs:subClassOf ?superclass }
|
|
131
132
|
|
|
132
133
|
- $id: https://iolanta.tech/cli/textual
|
|
133
|
-
iolanta:when-no-facet-found:
|
|
134
|
+
iolanta:when-no-facet-found: pkg:pypi/iolanta#textual-no-facet-found
|
|
@@ -136,7 +136,7 @@ class PageSwitcher(IolantaWidgetMixin, ContentSwitcher): # noqa: WPS214
|
|
|
136
136
|
for facet in other_facets
|
|
137
137
|
]
|
|
138
138
|
|
|
139
|
-
facet_class = iolanta.facet_resolver
|
|
139
|
+
facet_class = iolanta.facet_resolver.resolve(facet_iri)
|
|
140
140
|
|
|
141
141
|
facet = facet_class(
|
|
142
142
|
iri=self.iri,
|
iolanta/facets/title/facets.py
CHANGED
|
@@ -10,6 +10,7 @@ PRIORITIES = [ # noqa: WPS407
|
|
|
10
10
|
'rdfs_label',
|
|
11
11
|
'foaf_name',
|
|
12
12
|
'literal_form',
|
|
13
|
+
'preferred_label',
|
|
13
14
|
]
|
|
14
15
|
|
|
15
16
|
|
|
@@ -20,7 +21,7 @@ class TitleFacet(Facet[str]):
|
|
|
20
21
|
"""Render title of a thing."""
|
|
21
22
|
choices = self.stored_query(
|
|
22
23
|
'title.sparql',
|
|
23
|
-
iri=self.
|
|
24
|
+
iri=self.this,
|
|
24
25
|
language=self.iolanta.language,
|
|
25
26
|
)
|
|
26
27
|
|
|
@@ -28,4 +28,9 @@ SELECT * WHERE {
|
|
|
28
28
|
$iri <https://www.w3.org/2008/05/skos-xl#literalForm> ?literal_form .
|
|
29
29
|
FILTER (!lang(?literal_form) || lang(?literal_form) = $language)
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
OPTIONAL {
|
|
33
|
+
$iri <https://www.w3.org/2004/02/skos/core#prefLabel> ?preferred_label .
|
|
34
|
+
FILTER (!lang(?preferred_label) || lang(?preferred_label) = $language)
|
|
35
|
+
}
|
|
31
36
|
}
|
iolanta/iolanta.py
CHANGED
|
@@ -39,23 +39,17 @@ from iolanta.query_result import (
|
|
|
39
39
|
SPARQLQueryArgument,
|
|
40
40
|
format_query_bindings,
|
|
41
41
|
)
|
|
42
|
+
from iolanta.resolvers.base import Resolver
|
|
43
|
+
from iolanta.resolvers.dispatch import SchemeDispatchResolver
|
|
44
|
+
from iolanta.resolvers.pypi import PyPIResolver
|
|
42
45
|
from iolanta.resolvers.python_import import PythonImportResolver
|
|
43
46
|
from iolanta.sparqlspace.processor import normalize_term
|
|
44
47
|
|
|
45
48
|
|
|
46
49
|
class LoggerProtocol(Protocol):
|
|
47
|
-
"""
|
|
48
|
-
Abstract Logger interface.
|
|
50
|
+
"""Abstract Logger interface that unites `loguru` & standard `logging`."""
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
def info( # noqa: WPS110
|
|
54
|
-
self,
|
|
55
|
-
message: str,
|
|
56
|
-
*args: Any,
|
|
57
|
-
**kwargs: Any,
|
|
58
|
-
) -> None:
|
|
52
|
+
def info(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
59
53
|
"""Log an INFO message."""
|
|
60
54
|
|
|
61
55
|
def error(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
@@ -85,8 +79,12 @@ class Iolanta: # noqa: WPS214
|
|
|
85
79
|
graph: ConjunctiveGraph = field(default_factory=_create_default_graph)
|
|
86
80
|
force_plugins: List[Type[Plugin]] = field(default_factory=list)
|
|
87
81
|
|
|
88
|
-
facet_resolver:
|
|
89
|
-
default_factory=
|
|
82
|
+
facet_resolver: Resolver = field(
|
|
83
|
+
default_factory=functools.partial(
|
|
84
|
+
SchemeDispatchResolver,
|
|
85
|
+
python=PythonImportResolver,
|
|
86
|
+
pkg=PyPIResolver,
|
|
87
|
+
),
|
|
90
88
|
)
|
|
91
89
|
|
|
92
90
|
logger: LoggerProtocol = loguru.logger
|
|
@@ -103,11 +101,7 @@ class Iolanta: # noqa: WPS214
|
|
|
103
101
|
|
|
104
102
|
@functools.cached_property
|
|
105
103
|
def plugins(self) -> List[Plugin]:
|
|
106
|
-
"""
|
|
107
|
-
Construct a list of installed plugin instances.
|
|
108
|
-
|
|
109
|
-
# FIXME: Get rid of those.
|
|
110
|
-
"""
|
|
104
|
+
"""Construct a list of installed plugin instances."""
|
|
111
105
|
return [
|
|
112
106
|
plugin_class(iolanta=self)
|
|
113
107
|
for plugin_class in self.plugin_classes
|
|
@@ -118,28 +112,7 @@ class Iolanta: # noqa: WPS214
|
|
|
118
112
|
query_text: str,
|
|
119
113
|
**kwargs: SPARQLQueryArgument,
|
|
120
114
|
) -> QueryResult:
|
|
121
|
-
"""
|
|
122
|
-
Run a SPARQL `SELECT`, `CONSTRUCT`, or `ASK` query.
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
query_text: The SPARQL text;
|
|
126
|
-
**kwargs: bind variables in the query to values if necessary. For
|
|
127
|
-
example:
|
|
128
|
-
|
|
129
|
-
```python
|
|
130
|
-
iolanta.query(
|
|
131
|
-
'SELECT ?title WHERE { ?page rdfs:label ?title }',
|
|
132
|
-
?page=page_iri,
|
|
133
|
-
)
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Returns:
|
|
137
|
-
Results of the query:
|
|
138
|
-
|
|
139
|
-
- a graph for `CONSTRUCT`,
|
|
140
|
-
- a list of dicts for `SELECT`,
|
|
141
|
-
- or a boolean for `ASK`.
|
|
142
|
-
"""
|
|
115
|
+
"""Run a SPARQL `SELECT`, `CONSTRUCT`, or `ASK` query."""
|
|
143
116
|
try:
|
|
144
117
|
sparql_result: SPARQLResult = self.graph.query(
|
|
145
118
|
query_text,
|
|
@@ -231,11 +204,7 @@ class Iolanta: # noqa: WPS214
|
|
|
231
204
|
return self
|
|
232
205
|
|
|
233
206
|
def infer(self, closure_class=None) -> 'Iolanta':
|
|
234
|
-
"""
|
|
235
|
-
Apply inference.
|
|
236
|
-
|
|
237
|
-
TODO Remove this. Or use `reasonable`. Not sure.
|
|
238
|
-
"""
|
|
207
|
+
"""Apply inference."""
|
|
239
208
|
return self
|
|
240
209
|
|
|
241
210
|
def bind_namespaces(self):
|
|
@@ -258,11 +227,7 @@ class Iolanta: # noqa: WPS214
|
|
|
258
227
|
|
|
259
228
|
@functools.cached_property
|
|
260
229
|
def context_paths(self) -> Iterable[Path]:
|
|
261
|
-
"""
|
|
262
|
-
Compile list of context files.
|
|
263
|
-
|
|
264
|
-
FIXME: Get rid of those.
|
|
265
|
-
"""
|
|
230
|
+
"""Compile list of context files."""
|
|
266
231
|
directory = Path(__file__).parent / 'data'
|
|
267
232
|
|
|
268
233
|
yield directory / 'context.yaml'
|
|
@@ -272,11 +237,7 @@ class Iolanta: # noqa: WPS214
|
|
|
272
237
|
yield path
|
|
273
238
|
|
|
274
239
|
def add_files_from_plugins(self):
|
|
275
|
-
"""
|
|
276
|
-
Load files from plugins.
|
|
277
|
-
|
|
278
|
-
FIXME: Get rid of plugins.
|
|
279
|
-
"""
|
|
240
|
+
"""Load files from plugins."""
|
|
280
241
|
for plugin in self.plugins:
|
|
281
242
|
try:
|
|
282
243
|
self.add(plugin.data_files)
|
|
@@ -285,14 +246,24 @@ class Iolanta: # noqa: WPS214
|
|
|
285
246
|
f'Cannot load {plugin} plugin data files: {error}',
|
|
286
247
|
)
|
|
287
248
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
249
|
+
@property
|
|
250
|
+
def facet_classes(self):
|
|
251
|
+
"""Get all registered facet classes."""
|
|
252
|
+
return entry_points.plugins('iolanta.facets')
|
|
291
253
|
|
|
292
|
-
|
|
293
|
-
"""
|
|
254
|
+
def add_files_from_facets(self):
|
|
255
|
+
"""Add files from all registered facets to the graph."""
|
|
256
|
+
for facet_class in self.facet_classes:
|
|
257
|
+
try:
|
|
258
|
+
self.add(facet_class.META)
|
|
259
|
+
except AttributeError:
|
|
260
|
+
pass
|
|
261
|
+
|
|
262
|
+
def __post_init__(self):
|
|
263
|
+
"""Initialize after instance creation."""
|
|
294
264
|
self.bind_namespaces()
|
|
295
265
|
self.add_files_from_plugins()
|
|
266
|
+
self.add_files_from_facets()
|
|
296
267
|
if self.project_root:
|
|
297
268
|
self.add(self.project_root)
|
|
298
269
|
|
|
@@ -318,7 +289,7 @@ class Iolanta: # noqa: WPS214
|
|
|
318
289
|
as_datatype=as_datatype,
|
|
319
290
|
).facet_and_output_datatype
|
|
320
291
|
|
|
321
|
-
facet_class = self.facet_resolver
|
|
292
|
+
facet_class = self.facet_resolver.resolve(found['facet'])
|
|
322
293
|
|
|
323
294
|
facet = facet_class(
|
|
324
295
|
iri=node,
|
|
@@ -375,11 +346,7 @@ class Iolanta: # noqa: WPS214
|
|
|
375
346
|
) from err
|
|
376
347
|
|
|
377
348
|
def node_as_qname(self, node: Node):
|
|
378
|
-
"""
|
|
379
|
-
Render node as a QName if possible.
|
|
380
|
-
|
|
381
|
-
Return the node as is, if it is not.
|
|
382
|
-
"""
|
|
349
|
+
"""Render node as a QName if possible."""
|
|
383
350
|
qname = node_to_qname(node, self.graph)
|
|
384
351
|
return f'{qname.namespace_name}:{qname.term}' if isinstance(
|
|
385
352
|
qname,
|
|
File without changes
|
iolanta/mermaid/facet.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
|
|
5
|
+
import boltons
|
|
6
|
+
import cachetools
|
|
7
|
+
import funcy
|
|
8
|
+
from boltons.cacheutils import cached, cachedmethod
|
|
9
|
+
from pydantic import AnyUrl
|
|
10
|
+
from rdflib import BNode, Literal, Node, URIRef
|
|
11
|
+
|
|
12
|
+
from iolanta import Facet
|
|
13
|
+
from iolanta.mermaid.models import (
|
|
14
|
+
Diagram,
|
|
15
|
+
MermaidBlankNode,
|
|
16
|
+
MermaidEdge,
|
|
17
|
+
MermaidLiteral,
|
|
18
|
+
MermaidScalar,
|
|
19
|
+
MermaidSubgraph,
|
|
20
|
+
MermaidURINode,
|
|
21
|
+
)
|
|
22
|
+
from iolanta.models import NotLiteralNode
|
|
23
|
+
from iolanta.namespaces import DATATYPES
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def filter_edges(edges: Iterable[MermaidEdge]) -> Iterable[MermaidEdge]:
|
|
27
|
+
for edge in edges:
|
|
28
|
+
if isinstance(edge.target, MermaidLiteral) and edge.source.title == edge.target.title:
|
|
29
|
+
continue
|
|
30
|
+
|
|
31
|
+
yield edge
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def filter_nodes(edges: Iterable[MermaidEdge], except_uris: Iterable[NotLiteralNode]) -> Iterable[MermaidURINode | MermaidLiteral | MermaidBlankNode]:
|
|
35
|
+
nodes = [
|
|
36
|
+
node
|
|
37
|
+
for edge in edges
|
|
38
|
+
for node in edge.nodes
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
literals_in_edges = {edge.target for edge in edges if isinstance(edge.target, MermaidLiteral)}
|
|
42
|
+
for node in nodes:
|
|
43
|
+
if isinstance(node, MermaidLiteral) and node not in literals_in_edges:
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
if isinstance(node, MermaidURINode) and node.uri in except_uris:
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
if isinstance(node, MermaidBlankNode) and node.node in except_uris:
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
if isinstance(node, MermaidSubgraph):
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
yield node
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Mermaid(Facet[str]):
|
|
59
|
+
"""Mermaid diagram."""
|
|
60
|
+
|
|
61
|
+
META = Path(__file__).parent / 'mermaid.yamlld'
|
|
62
|
+
|
|
63
|
+
def as_mermaid(self, node: Node):
|
|
64
|
+
match node:
|
|
65
|
+
case URIRef() as uri:
|
|
66
|
+
if uri in self.subgraph_uris:
|
|
67
|
+
return MermaidSubgraph(children=[], uri=uri, title=self.render(uri, as_datatype=DATATYPES.title))
|
|
68
|
+
|
|
69
|
+
return MermaidURINode(uri=uri, url=AnyUrl(uri), title=self.render(uri, as_datatype=DATATYPES.title))
|
|
70
|
+
case Literal() as literal:
|
|
71
|
+
return MermaidLiteral(literal=literal)
|
|
72
|
+
case BNode() as bnode:
|
|
73
|
+
return MermaidBlankNode(node=bnode, title=self.render(bnode, as_datatype=DATATYPES.title))
|
|
74
|
+
case unknown:
|
|
75
|
+
unknown_type = type(unknown)
|
|
76
|
+
raise ValueError(f'Unknown something: {unknown} ({unknown_type})')
|
|
77
|
+
|
|
78
|
+
def construct_mermaid_for_graph(self, graph: URIRef) -> Iterable[MermaidScalar]:
|
|
79
|
+
"""Render graph as mermaid."""
|
|
80
|
+
rows = self.stored_query('graph.sparql', this=graph)
|
|
81
|
+
edges = [
|
|
82
|
+
MermaidEdge(
|
|
83
|
+
source=self.as_mermaid(row['s']),
|
|
84
|
+
target=self.as_mermaid(row['o']),
|
|
85
|
+
title=self.render(row['p'], as_datatype=DATATYPES.title),
|
|
86
|
+
predicate=row['p'],
|
|
87
|
+
) for row in rows
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
edges = list(filter_edges(edges))
|
|
91
|
+
nodes = list(
|
|
92
|
+
filter_nodes(
|
|
93
|
+
edges=edges,
|
|
94
|
+
except_uris=self.subgraph_uris,
|
|
95
|
+
),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return *nodes, *edges
|
|
99
|
+
|
|
100
|
+
@functools.cached_property
|
|
101
|
+
def subgraph_uris(self) -> set[NotLiteralNode]:
|
|
102
|
+
return set(
|
|
103
|
+
funcy.pluck(
|
|
104
|
+
'subgraph',
|
|
105
|
+
self.stored_query('subgraphs.sparql', this=self.this),
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def construct_mermaid_subgraphs(self) -> Iterable[MermaidSubgraph]:
|
|
110
|
+
for subgraph_uri in self.subgraph_uris:
|
|
111
|
+
children = list(self.construct_mermaid_for_graph(subgraph_uri))
|
|
112
|
+
if children:
|
|
113
|
+
title = self.render(subgraph_uri, as_datatype=DATATYPES.title)
|
|
114
|
+
yield MermaidSubgraph(
|
|
115
|
+
children=children,
|
|
116
|
+
uri=subgraph_uri,
|
|
117
|
+
title=title,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def show(self) -> str:
|
|
121
|
+
"""Render mermaid diagram."""
|
|
122
|
+
direct_children = self.construct_mermaid_for_graph(self.this)
|
|
123
|
+
subgraphs = self.construct_mermaid_subgraphs()
|
|
124
|
+
return str(Diagram(children=[*direct_children, *subgraphs]))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"@context":
|
|
2
|
+
"@import": https://json-ld.org/contexts/dollar-convenience.jsonld
|
|
3
|
+
vann: https://purl.org/vocab/vann/
|
|
4
|
+
foaf: https://xmlns.com/foaf/0.1/
|
|
5
|
+
owl: https://www.w3.org/2002/07/owl#
|
|
6
|
+
iolanta: https://iolanta.tech/
|
|
7
|
+
rdfs: "https://www.w3.org/2000/01/rdf-schema#"
|
|
8
|
+
rdf: https://www.w3.org/1999/02/22-rdf-syntax-ns#
|
|
9
|
+
dcterms: https://purl.org/dc/terms/
|
|
10
|
+
dcam: https://purl.org/dc/dcam/
|
|
11
|
+
|
|
12
|
+
iolanta:outputs:
|
|
13
|
+
"@type": "@id"
|
|
14
|
+
|
|
15
|
+
iolanta:when-no-facet-found:
|
|
16
|
+
"@type": "@id"
|
|
17
|
+
|
|
18
|
+
$: rdfs:label
|
|
19
|
+
→:
|
|
20
|
+
"@type": "@id"
|
|
21
|
+
"@id": iolanta:outputs
|
|
22
|
+
|
|
23
|
+
⊆:
|
|
24
|
+
"@type": "@id"
|
|
25
|
+
"@id": rdfs:subClassOf
|
|
26
|
+
|
|
27
|
+
⪯:
|
|
28
|
+
"@type": "@id"
|
|
29
|
+
"@id": iolanta:is-preferred-over
|
|
30
|
+
|
|
31
|
+
↦: iolanta:matches
|
|
32
|
+
iolanta:hasDefaultFacet:
|
|
33
|
+
"@type": "@id"
|
|
34
|
+
|
|
35
|
+
$id: pkg:pypi/iolanta#mermaid-graph
|
|
36
|
+
$: Mermaid Graph
|
|
37
|
+
→:
|
|
38
|
+
$id: https://iolanta.tech/datatypes/mermaid
|
|
39
|
+
$: Mermaid
|
|
40
|
+
↦:
|
|
41
|
+
- ASK WHERE { GRAPH $this { ?s ?p ?o } }
|
|
42
|
+
- ASK WHERE { $this iolanta:has-sub-graph ?subgraph }
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import hashlib
|
|
3
|
+
import textwrap
|
|
4
|
+
import urllib.parse
|
|
5
|
+
|
|
6
|
+
from documented import Documented
|
|
7
|
+
from pydantic import AnyUrl, BaseModel
|
|
8
|
+
from rdflib import BNode, Literal, URIRef
|
|
9
|
+
|
|
10
|
+
from iolanta.models import NotLiteralNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Direction(enum.StrEnum):
|
|
14
|
+
"""Mermaid diagram direction."""
|
|
15
|
+
|
|
16
|
+
TB = 'TB'
|
|
17
|
+
LR = 'LR'
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class MermaidURINode(Documented, BaseModel, arbitrary_types_allowed=True, frozen=True):
|
|
21
|
+
"""
|
|
22
|
+
{self.id}{self.maybe_title}
|
|
23
|
+
click {self.id} "{self.url}"
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
uri: URIRef
|
|
27
|
+
url: AnyUrl
|
|
28
|
+
title: str = ''
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def maybe_title(self):
|
|
32
|
+
return self.title and f'({self.title})'
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def id(self):
|
|
36
|
+
return urllib.parse.unquote(str(self.url)).strip('/').replace(':', '_').replace('/', '_').replace('.', '_').replace('#', '_')
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MermaidLiteral(Documented, BaseModel, arbitrary_types_allowed=True, frozen=True):
|
|
40
|
+
"""{self.id}[[{self.title}]]"""
|
|
41
|
+
|
|
42
|
+
literal: Literal
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def title(self) -> str:
|
|
46
|
+
return str(self.literal) or 'EMPTY'
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def id(self) -> str:
|
|
50
|
+
value_hash = hashlib.md5(str(self.literal.value).encode()).hexdigest()
|
|
51
|
+
return f'Literal-{value_hash}'
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MermaidBlankNode(Documented, BaseModel, arbitrary_types_allowed=True):
|
|
55
|
+
"""{self.id}({self.title})"""
|
|
56
|
+
|
|
57
|
+
node: BNode
|
|
58
|
+
title: str
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def id(self) -> str:
|
|
62
|
+
return self.node.replace('_:', '')
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class MermaidEdge(Documented, BaseModel, arbitrary_types_allowed=True):
|
|
66
|
+
"""
|
|
67
|
+
{self.source.id} --- {self.id}(["{self.title}"])--> {self.target.id}
|
|
68
|
+
click {self.id} "{self.predicate}"
|
|
69
|
+
class {self.id} predicate
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
source: 'MermaidURINode | MermaidBlankNode | MermaidSubgraph'
|
|
73
|
+
target: 'MermaidURINode | MermaidLiteral | MermaidBlankNode | MermaidSubgraph'
|
|
74
|
+
predicate: URIRef
|
|
75
|
+
title: str
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def id(self) -> str:
|
|
79
|
+
return hashlib.md5(f'{self.source.id}{self.predicate}{self.target.id}'.encode()).hexdigest()
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def nodes(self):
|
|
83
|
+
return [self.source, self.target]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
MermaidScalar = MermaidLiteral | MermaidBlankNode | MermaidURINode | MermaidEdge
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class MermaidSubgraph(Documented, BaseModel, arbitrary_types_allowed=True, frozen=True):
|
|
90
|
+
"""
|
|
91
|
+
subgraph {self.id}["{self.title}"]
|
|
92
|
+
direction {self.direction}
|
|
93
|
+
{self.formatted_body}
|
|
94
|
+
end
|
|
95
|
+
"""
|
|
96
|
+
children: list[MermaidScalar]
|
|
97
|
+
uri: NotLiteralNode
|
|
98
|
+
title: str
|
|
99
|
+
direction: Direction = Direction.LR
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def id(self):
|
|
103
|
+
uri_hash = hashlib.md5(str(self.uri).encode()).hexdigest()
|
|
104
|
+
return f'subgraph_{uri_hash}'
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def formatted_body(self):
|
|
108
|
+
return textwrap.indent(
|
|
109
|
+
'\n'.join(map(str, self.children)),
|
|
110
|
+
prefix=' ',
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class Diagram(Documented, BaseModel):
|
|
115
|
+
"""
|
|
116
|
+
graph {self.direction}
|
|
117
|
+
{self.formatted_body}
|
|
118
|
+
classDef predicate fill:none,stroke:none,stroke-width:0px;
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
children: list[MermaidScalar | MermaidSubgraph]
|
|
122
|
+
direction: Direction = Direction.LR
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def formatted_body(self):
|
|
126
|
+
return textwrap.indent(
|
|
127
|
+
'\n'.join(map(str, self.children)),
|
|
128
|
+
prefix=' ',
|
|
129
|
+
)
|
iolanta/namespaces.py
CHANGED
|
@@ -3,7 +3,7 @@ import rdflib
|
|
|
3
3
|
LOCAL = rdflib.Namespace('local:')
|
|
4
4
|
IOLANTA = rdflib.Namespace('https://iolanta.tech/')
|
|
5
5
|
DATATYPES = rdflib.Namespace('https://iolanta.tech/datatypes/')
|
|
6
|
-
|
|
6
|
+
IOLANTA_FACETS = rdflib.Namespace('pkg:pypi/iolanta#')
|
|
7
7
|
NP = rdflib.Namespace('https://www.nanopub.org/nschema#')
|
|
8
8
|
RDFG = rdflib.Namespace('https://www.w3.org/2004/03/trix/rdfg-1/')
|
|
9
9
|
SDO = rdflib.SDO
|
iolanta/resolvers/base.py
CHANGED
|
@@ -8,6 +8,7 @@ from iolanta.facets.facet import Facet
|
|
|
8
8
|
|
|
9
9
|
class Resolver:
|
|
10
10
|
"""Resolve facet IRIs into classes."""
|
|
11
|
+
|
|
11
12
|
@abstractmethod
|
|
12
|
-
def
|
|
13
|
+
def resolve(self, uri: URIRef) -> Type[Facet]:
|
|
13
14
|
"""Find a resolver by IRI."""
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import Type
|
|
2
|
+
|
|
3
|
+
from rdflib import URIRef
|
|
4
|
+
from yarl import URL
|
|
5
|
+
|
|
6
|
+
from iolanta import Facet
|
|
7
|
+
from iolanta.resolvers.base import Resolver
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SchemeDispatchResolver(Resolver):
|
|
11
|
+
"""
|
|
12
|
+
A resolver that dispatches to other resolvers based on URI scheme.
|
|
13
|
+
|
|
14
|
+
For example, 'pkg:' URIs are handled by PyPIResolver, while
|
|
15
|
+
'python:' URIs are handled by PythonImportResolver.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, **resolver_by_scheme: Type[Resolver]):
|
|
19
|
+
"""
|
|
20
|
+
Initialize with a mapping of URI schemes to resolver classes.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
**resolver_by_scheme: Mapping of URI schemes to resolver classes.
|
|
24
|
+
For example: python=PythonImportResolver.
|
|
25
|
+
"""
|
|
26
|
+
self.resolver_by_scheme = resolver_by_scheme
|
|
27
|
+
|
|
28
|
+
def resolve(self, uri: URIRef) -> Type[Facet]:
|
|
29
|
+
"""
|
|
30
|
+
Find a resolver by IRI, dispatching to the appropriate scheme handler.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
uri: The URI to resolve.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Facet class resolved by the appropriate scheme handler.
|
|
37
|
+
"""
|
|
38
|
+
url = URL(uri)
|
|
39
|
+
resolver_class = self.resolver_by_scheme[url.scheme]
|
|
40
|
+
resolver = resolver_class()
|
|
41
|
+
return resolver.resolve(uri)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from importlib import metadata
|
|
5
|
+
from typing import Type
|
|
6
|
+
|
|
7
|
+
from packageurl import PackageURL
|
|
8
|
+
from rdflib import URIRef
|
|
9
|
+
|
|
10
|
+
from iolanta import Facet
|
|
11
|
+
from iolanta.resolvers.base import Resolver
|
|
12
|
+
|
|
13
|
+
ENTRY_POINT_GROUP = 'iolanta.facets'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _norm(name: str) -> str:
|
|
17
|
+
"""Normalize package name by replacing common separators with dashes."""
|
|
18
|
+
return re.sub('[-_.]+', '-', name).lower()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MissingFacetError(RuntimeError):
|
|
22
|
+
"""Raised when a required facet is not found in the installed packages."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PyPIResolver(Resolver):
|
|
26
|
+
"""Resolve facet IRIs into classes from PyPI packages."""
|
|
27
|
+
|
|
28
|
+
def resolve(self, uri: URIRef) -> Type[Facet]:
|
|
29
|
+
"""Find and load a Facet class from a PyPI package."""
|
|
30
|
+
dist_name, facet_name = self._get_package_info(uri)
|
|
31
|
+
dist = self._get_distribution(dist_name, facet_name)
|
|
32
|
+
entry_point = self._find_facet_entry_point(dist, facet_name)
|
|
33
|
+
facet_class = entry_point.load()
|
|
34
|
+
self._validate_facet_class(
|
|
35
|
+
facet_class=facet_class,
|
|
36
|
+
facet_name=facet_name,
|
|
37
|
+
dist_name=dist.metadata['Name'],
|
|
38
|
+
)
|
|
39
|
+
return facet_class
|
|
40
|
+
|
|
41
|
+
def _get_package_info(self, uri: URIRef) -> tuple[str, str]:
|
|
42
|
+
"""Extract and validate package information from URI."""
|
|
43
|
+
package_url = PackageURL.from_string(str(uri))
|
|
44
|
+
if package_url.type != 'pypi':
|
|
45
|
+
raise NotImplementedError(f'{package_url.type} is not supported')
|
|
46
|
+
|
|
47
|
+
dist_name = _norm(package_url.name or '')
|
|
48
|
+
facet_name = package_url.subpath
|
|
49
|
+
|
|
50
|
+
if not dist_name or not facet_name:
|
|
51
|
+
raise ValueError('PURL must be pkg:pypi/<dist>#<FacetName>')
|
|
52
|
+
|
|
53
|
+
return dist_name, facet_name
|
|
54
|
+
|
|
55
|
+
def _get_distribution(
|
|
56
|
+
self,
|
|
57
|
+
dist_name: str,
|
|
58
|
+
facet_name: str,
|
|
59
|
+
) -> metadata.Distribution:
|
|
60
|
+
"""Get package distribution and verify it exists."""
|
|
61
|
+
try:
|
|
62
|
+
return metadata.distribution(dist_name)
|
|
63
|
+
except metadata.PackageNotFoundError as exc:
|
|
64
|
+
raise MissingFacetError(
|
|
65
|
+
f'This page requires `{dist_name}` (facet `{facet_name}`). '
|
|
66
|
+
f'Install with: pip install "{dist_name}"',
|
|
67
|
+
) from exc
|
|
68
|
+
|
|
69
|
+
def _find_facet_entry_point(
|
|
70
|
+
self,
|
|
71
|
+
dist: metadata.Distribution,
|
|
72
|
+
facet_name: str,
|
|
73
|
+
) -> metadata.EntryPoint:
|
|
74
|
+
"""Find the facet entry point in the distribution."""
|
|
75
|
+
matching_entry_points = [
|
|
76
|
+
entry_point
|
|
77
|
+
for entry_point in dist.entry_points
|
|
78
|
+
if (
|
|
79
|
+
entry_point.group == ENTRY_POINT_GROUP
|
|
80
|
+
and entry_point.name == facet_name
|
|
81
|
+
)
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
if not matching_entry_points:
|
|
85
|
+
dist_name = dist.metadata['Name']
|
|
86
|
+
msg = (
|
|
87
|
+
f'Facet `{facet_name}` not found in `{dist_name}` '
|
|
88
|
+
f'{dist.version}. Ensure it exposes entry point '
|
|
89
|
+
f'`{ENTRY_POINT_GROUP}` named `{facet_name}`.'
|
|
90
|
+
)
|
|
91
|
+
raise MissingFacetError(msg)
|
|
92
|
+
|
|
93
|
+
return matching_entry_points[0]
|
|
94
|
+
|
|
95
|
+
def _validate_facet_class(
|
|
96
|
+
self,
|
|
97
|
+
facet_class: Type[Facet],
|
|
98
|
+
facet_name: str,
|
|
99
|
+
dist_name: str,
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Validate that the loaded class is a Facet subclass."""
|
|
102
|
+
if not issubclass(facet_class, Facet):
|
|
103
|
+
raise TypeError(
|
|
104
|
+
f'Entry point `{facet_name}` in `{dist_name}` '
|
|
105
|
+
'is not a subclass of iolanta.Facet',
|
|
106
|
+
)
|
|
@@ -1,40 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
from typing import Type, cast
|
|
1
|
+
from typing import Type
|
|
3
2
|
|
|
4
3
|
from rdflib import URIRef
|
|
5
4
|
|
|
6
|
-
from iolanta.facets.errors import FacetNotCallable
|
|
7
5
|
from iolanta.facets.facet import Facet
|
|
8
6
|
from iolanta.resolvers.base import Resolver
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
class PythonImportResolver(Resolver):
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
'which {url} does not comply to.'.format(
|
|
21
|
-
url=url,
|
|
22
|
-
),
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
# It is impossible to use `yarl` for this operation because it (or,
|
|
26
|
-
# rather, one of upper classes from `urllib` that `yarl` depends
|
|
27
|
-
# upon)
|
|
28
|
-
# will lowercase the URL when parsing it - which means, irreversibly. We
|
|
29
|
-
# have to resort to plain string manipulation.
|
|
30
|
-
import_path = url.replace('python://', '').strip('/')
|
|
31
|
-
|
|
32
|
-
facet = cast(Type['iolanta.Facet'], pydoc.locate(import_path))
|
|
33
|
-
|
|
34
|
-
if not callable(facet):
|
|
35
|
-
raise FacetNotCallable(
|
|
36
|
-
path=import_path,
|
|
37
|
-
facet=facet,
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
return facet
|
|
10
|
+
"""Resolve facet IRIs into classes by importing Python modules."""
|
|
11
|
+
|
|
12
|
+
def resolve(self, uri: URIRef) -> Type[Facet]:
|
|
13
|
+
"""Find a resolver by IRI in python:<module>.<class> format."""
|
|
14
|
+
raise NotImplementedError(
|
|
15
|
+
f'{uri} URL for facet import is unsupported anymore '
|
|
16
|
+
f'due to security reasons.',
|
|
17
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: iolanta
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Semantic Web browser
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Anatoly Scherbakov
|
|
@@ -24,6 +24,7 @@ Requires-Dist: more-itertools (>=9.0.0)
|
|
|
24
24
|
Requires-Dist: nanopub (>=2.0.1)
|
|
25
25
|
Requires-Dist: owlrl (>=6.0.2)
|
|
26
26
|
Requires-Dist: oxrdflib (>=0.4.0)
|
|
27
|
+
Requires-Dist: packageurl-python (>=0.17.5)
|
|
27
28
|
Requires-Dist: python-frontmatter (>=0.5.0)
|
|
28
29
|
Requires-Dist: rdflib (<8.0)
|
|
29
30
|
Requires-Dist: reasonable (>=0.2.6)
|
|
@@ -31,7 +32,7 @@ Requires-Dist: requests (>=2.25.1)
|
|
|
31
32
|
Requires-Dist: rich (>=13.3.1)
|
|
32
33
|
Requires-Dist: textual (>=0.83.0)
|
|
33
34
|
Requires-Dist: typer (>=0.9.0)
|
|
34
|
-
Requires-Dist: watchfiles (>=1.0.4
|
|
35
|
+
Requires-Dist: watchfiles (>=1.0.4)
|
|
35
36
|
Requires-Dist: yaml-ld (>=1.1.10)
|
|
36
37
|
Requires-Dist: yarl (>=1.9.4)
|
|
37
38
|
Description-Content-Type: text/markdown
|
|
@@ -6,17 +6,15 @@ iolanta/cli/formatters/choose.py,sha256=LWzsO_9IBSSgYNIyLlItkp8TNvpW3v6YCQ8-6kbI
|
|
|
6
6
|
iolanta/cli/formatters/csv.py,sha256=ceJ_DTz0beqeK-d6FPBQqqjXrziEfF0FRSLoGZCt_fs,760
|
|
7
7
|
iolanta/cli/formatters/json.py,sha256=Og5B9UrSM_0NWqW5Afpsy6WH8ZfYgPMVXYvT3i-43Jc,748
|
|
8
8
|
iolanta/cli/formatters/pretty.py,sha256=IypZRAr2vNqcXFY6NOIc75mpyfpFWh56aCBlOPDDieQ,2901
|
|
9
|
-
iolanta/cli/main.py,sha256=
|
|
9
|
+
iolanta/cli/main.py,sha256=GVtdBQjAOs6VlsE_uztTXHbmRXUk_S6gN4LWqACJGS8,3157
|
|
10
10
|
iolanta/cli/models.py,sha256=cjbpowdzI4wAP0DUk3qoVHyimk6AZwlXi9CGmusZTuM,159
|
|
11
11
|
iolanta/cli/pretty_print.py,sha256=M6E3TmhzA6JY5GeUVmDZLmOh5u70-393PVit4voFKDI,977
|
|
12
12
|
iolanta/context.py,sha256=bZR-tbZIrDQ-Vby01PMDZ6ifxM-0YMK68RJvAsyqCTs,507
|
|
13
13
|
iolanta/conversions.py,sha256=hbLwRF1bAbOxy17eMWLHhYksbdCWN-v4-0y0wn3XSSg,1185
|
|
14
|
-
iolanta/data/cli.yaml,sha256=TsnldYXoY5GIzoNuPDvwBKGw8eAEForZW1FCKqKI0Kg,1029
|
|
15
14
|
iolanta/data/context.yaml,sha256=LUBasiBKgQeGAYjYV_T5XvgPlrdzACeKaZwY_rKzjtI,1636
|
|
16
|
-
iolanta/data/graph-triples.yamlld,sha256=
|
|
17
|
-
iolanta/data/html.yaml,sha256=hVFdLWLy8FMY8xpOrJMYc-tE3S0Nq83xuxVkjRW_7rI,517
|
|
15
|
+
iolanta/data/graph-triples.yamlld,sha256=rtn-HfbijaqbmjCrKv-2pVV_aaJhB_9_OqXA_yLznCs,209
|
|
18
16
|
iolanta/data/iolanta.yaml,sha256=xubIFBNU02lmFXhgOSuyQwUcZD3xCqVfeVAZMvOxKbI,1433
|
|
19
|
-
iolanta/data/textual-browser.yaml,sha256=
|
|
17
|
+
iolanta/data/textual-browser.yaml,sha256=nJbDS0B3G-emM9vCu2DIlWZBhzmWbO3zrWkt_Rmv5uA,3700
|
|
20
18
|
iolanta/ensure_is_context.py,sha256=9aok8asyEx7KPesOR28VBDb3Ch9kfc3eoCpQSJwj07U,717
|
|
21
19
|
iolanta/entry_points.py,sha256=DZbf-udlEwELFGqeWENj0M2BOUPOWlmGJdqyaEtnot0,504
|
|
22
20
|
iolanta/errors.py,sha256=t_RltahnoEvcytVa1BOq2MADgHps3JNd_h5-SFu7_wM,1250
|
|
@@ -53,7 +51,7 @@ iolanta/facets/textual_browser/home.py,sha256=GfDD1G2HiwdLkPkNdcYRqVIxDl5tWH9few
|
|
|
53
51
|
iolanta/facets/textual_browser/location.py,sha256=w0La8bVTpJiVo1_hFTDQeIUdDdqfhYnoihuZW-f130c,205
|
|
54
52
|
iolanta/facets/textual_browser/models.py,sha256=DqTBjhkkTt5mNwqr4DzNbPSqzV-QtNqfKj7wpn6T3ao,173
|
|
55
53
|
iolanta/facets/textual_browser/page.py,sha256=NkcQ5rSKZRbp63C8ozgsR_iVhcKHGv_SytUCQyGa7ss,786
|
|
56
|
-
iolanta/facets/textual_browser/page_switcher.py,sha256=
|
|
54
|
+
iolanta/facets/textual_browser/page_switcher.py,sha256=hZT9FFMXJf7t8V2ztsqsNNCmGY8CPx39mlEKJjhRsPA,9958
|
|
57
55
|
iolanta/facets/textual_class/__init__.py,sha256=tiL0p-3JspGcBRj4qa3rmoBFAuadk71l2ja2lJN6CEs,75
|
|
58
56
|
iolanta/facets/textual_class/facets.py,sha256=tascZiue3tbYfpIsnP8yY2dgXQIQzoQ_HPzU7DbVbao,5999
|
|
59
57
|
iolanta/facets/textual_class/sparql/instances.sparql,sha256=EW4BmuNxiRDA8djuEtvZpLLJlNSIshvId4ExlaYPcZE,106
|
|
@@ -90,22 +88,30 @@ iolanta/facets/textual_provenance/facets.py,sha256=vv3UQsI2duB36DW5Zkw3sqgAXBPmK
|
|
|
90
88
|
iolanta/facets/textual_provenance/sparql/graphs.sparql,sha256=B45uKFd-1vrBuMDSbTURjUUEjHt51vAbqdL4tUcgMvk,103
|
|
91
89
|
iolanta/facets/textual_provenance/sparql/triples.sparql,sha256=V-EdVuWbGHY3MspbJIMpwxPQautLDqJJV-AmihDjSHc,53
|
|
92
90
|
iolanta/facets/title/__init__.py,sha256=fxpkG-YvHDp6eiVL3o7BbwhPMZZe-1R2Qi6S36QCTf8,77
|
|
93
|
-
iolanta/facets/title/facets.py,sha256=
|
|
94
|
-
iolanta/facets/title/sparql/title.sparql,sha256=
|
|
91
|
+
iolanta/facets/title/facets.py,sha256=41cWNdisyb9dPJowSgTIQa-rmFCI4WUb-EYSFDr3GI0,836
|
|
92
|
+
iolanta/facets/title/sparql/title.sparql,sha256=VKHeE9NvV6sFgVGvsLQD9z__J4APj8TDHLT4Rk-Ascc,1102
|
|
95
93
|
iolanta/facets/wikibase_statement_title/__init__.py,sha256=_yk1akxgSJOiUBJIc8QGrD2vovvmx_iw_vJDuv1rD7M,91
|
|
96
94
|
iolanta/facets/wikibase_statement_title/facets.py,sha256=mUH7twlAgoeX7DgLQuRBQv4ORT6GWbN-0eJ1aliSfiQ,724
|
|
97
95
|
iolanta/facets/wikibase_statement_title/sparql/statement-title.sparql,sha256=n07DQWxKqB5c3CA4kacq2HSN0R0dLgnMnLP1AxMo5YA,320
|
|
98
|
-
iolanta/iolanta.py,sha256=
|
|
96
|
+
iolanta/iolanta.py,sha256=uM5vmK96Q_GsqXM-bBpQXoxwqs25K-YGYrZvWWFTsDA,11072
|
|
97
|
+
iolanta/mermaid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
|
+
iolanta/mermaid/facet.py,sha256=0SMzwkjtB3xjCQRW-XwOB5P7zVJdUCDv8c_XmanvqP0,4068
|
|
99
|
+
iolanta/mermaid/mermaid.yamlld,sha256=j5yQ6A1M_Fn4cq5WYISz8MfWw6skYdoB0lvcI68ahyw,968
|
|
100
|
+
iolanta/mermaid/models.py,sha256=v0loPqJYKpylVBaq89yFdk8iNxyzvN9Ih_ozAbhD4pk,3197
|
|
101
|
+
iolanta/mermaid/sparql/graph.sparql,sha256=mDGf05od3CUFhzI6rcqt5ZMVy-gSKDu-WxmV_zpIsVI,62
|
|
102
|
+
iolanta/mermaid/sparql/subgraphs.sparql,sha256=VuoOYr_ZtKXXRrBpAEJek0mBRzR9EV-KnKENgAbkzCs,71
|
|
99
103
|
iolanta/models.py,sha256=M-1dTxPwjTyUgQ4VCOiH6Q7ltNJiDPG0l1XQH430Omo,3250
|
|
100
|
-
iolanta/namespaces.py,sha256=
|
|
104
|
+
iolanta/namespaces.py,sha256=H_qYyxCqbIGMOczpT9vUBHumTW9QJ-Y6qXHbJgFwFP8,1210
|
|
101
105
|
iolanta/node_to_qname.py,sha256=a82_qpgT87cbekY_76tTkl4Z-6Rz6am4UGIQChUf9Y0,794
|
|
102
106
|
iolanta/parse_quads.py,sha256=nKUGS_OVpCA6PFA5rOd6Qa5ik_eQhCL-Mlv1zDXk_Co,4541
|
|
103
107
|
iolanta/plugin.py,sha256=MSxpuOIx93AgBahfS8bYh31MEgcwtUSQhj4Js7fgdSI,1096
|
|
104
108
|
iolanta/query_result.py,sha256=VLLBkewUEymtzfB0jeIeRE3Np6pAgo959RPgNsEmiq8,1545
|
|
105
109
|
iolanta/reformat_blank_nodes.py,sha256=MAVcXusUioKzAoTEHAMume5Gt9vBEpxJGrngqFzmkJI,712
|
|
106
110
|
iolanta/resolvers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
107
|
-
iolanta/resolvers/base.py,sha256=
|
|
108
|
-
iolanta/resolvers/
|
|
111
|
+
iolanta/resolvers/base.py,sha256=gkPHP85xRawmvZZtkZzFM0JjDRbZUKaTM_Zg2cdZWHk,293
|
|
112
|
+
iolanta/resolvers/dispatch.py,sha256=L2-ZQfusUgi-VuFdn0UA19ecEG1bZzV9PD2z2QJye1s,1204
|
|
113
|
+
iolanta/resolvers/pypi.py,sha256=BJA7PGPaFFIxisCp-1-pjWZYNTDRVA5am5Nn8O2WddM,3498
|
|
114
|
+
iolanta/resolvers/python_import.py,sha256=hFs2Fi7BDjdKllgrGJhTaqjfcT7Vaj0j1b45CJt21B0,522
|
|
109
115
|
iolanta/sparqlspace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
110
116
|
iolanta/sparqlspace/cli.py,sha256=pb6q03lzrU8OaZ4A3QAQEmaFYcQ_-sHUrPhRs6GE88w,1590
|
|
111
117
|
iolanta/sparqlspace/inference/wikibase-claim.sparql,sha256=JSawj3VTc9ZPoU9mvh1w1AMYb3cWyZ3x1rEYWUsKZ6A,209
|
|
@@ -115,7 +121,7 @@ iolanta/sparqlspace/sparqlspace.py,sha256=Y8_ZPXwuGEXbEes6XQjaQWA2Zv9y8SWxMPDFdq
|
|
|
115
121
|
iolanta/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
116
122
|
iolanta/widgets/description.py,sha256=98Qd3FwT9r8sYqKjl9ZEptaVX9jJ2ULWf0uy3j52p5o,800
|
|
117
123
|
iolanta/widgets/mixin.py,sha256=nDRCOc-gizCf1a5DAcYs4hW8eZEd6pHBPFsfm0ncv7E,251
|
|
118
|
-
iolanta-2.
|
|
119
|
-
iolanta-2.
|
|
120
|
-
iolanta-2.
|
|
121
|
-
iolanta-2.
|
|
124
|
+
iolanta-2.1.1.dist-info/METADATA,sha256=PC0tU9wgfi3BMHGEcI19gFVTUpQaIWxoGz2PcAoVx_8,2268
|
|
125
|
+
iolanta-2.1.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
126
|
+
iolanta-2.1.1.dist-info/entry_points.txt,sha256=H2bCnnscOwzl0QMoI86ifQ6hm1NhyYnrGQbMd0Kp6dA,1445
|
|
127
|
+
iolanta-2.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[console_scripts]
|
|
2
|
+
iolanta=iolanta.cli:app
|
|
3
|
+
sparqlspace=iolanta.sparqlspace.cli:app
|
|
4
|
+
|
|
5
|
+
[iolanta.facets]
|
|
6
|
+
boolean=iolanta.facets.generic:BoolLiteral
|
|
7
|
+
icon=iolanta.facets.icon:IconFacet
|
|
8
|
+
mermaid-graph=iolanta.mermaid.facet:Mermaid
|
|
9
|
+
qname=iolanta.facets.qname:QNameFacet
|
|
10
|
+
textual-browser=iolanta.facets.textual_browser:TextualBrowserFacet
|
|
11
|
+
textual-class=iolanta.facets.textual_class:Class
|
|
12
|
+
textual-graph=iolanta.facets.textual_graph:GraphFacet
|
|
13
|
+
textual-graph-triples=iolanta.facets.textual_graph_triples:GraphTriplesFacet
|
|
14
|
+
textual-inverse-properties=iolanta.facets.textual_default:InverseProperties
|
|
15
|
+
textual-link=iolanta.facets.textual_link:TextualLinkFacet
|
|
16
|
+
textual-nanopublication=iolanta.facets.textual_nanopublication:NanopublicationFacet
|
|
17
|
+
textual-no-facet-found=iolanta.facets.textual_no_facet_found:TextualNoFacetFound
|
|
18
|
+
textual-ontology=iolanta.facets.textual_ontology:OntologyFacet
|
|
19
|
+
textual-properties=iolanta.facets.textual_default:TextualDefaultFacet
|
|
20
|
+
textual-property-pairs=iolanta.facets.textual_property_pairs_table:TextualPropertyPairsTableFacet
|
|
21
|
+
textual-provenance=iolanta.facets.textual_provenance:TextualProvenanceFacet
|
|
22
|
+
title=iolanta.facets.title:TitleFacet
|
|
23
|
+
title-foaf-person=iolanta.facets.foaf_person_title:FOAFPersonTitle
|
|
24
|
+
wikibase-statement-title=iolanta.facets.wikibase_statement_title:WikibaseStatementTitle
|
|
25
|
+
|
|
26
|
+
[iolanta.plugins]
|
|
27
|
+
base=iolanta:IolantaBase
|
|
28
|
+
|
|
29
|
+
[rdf.plugins.queryprocessor]
|
|
30
|
+
sparqlspace=iolanta.sparqlspace.processor:GlobalSPARQLProcessor
|
|
31
|
+
|
iolanta/data/cli.yaml
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"@context":
|
|
2
|
-
"@import": https://json-ld.org/contexts/dollar-convenience.jsonld
|
|
3
|
-
vann: https://purl.org/vocab/vann/
|
|
4
|
-
foaf: https://xmlns.com/foaf/0.1/
|
|
5
|
-
owl: https://www.w3.org/2002/07/owl#
|
|
6
|
-
iolanta: https://iolanta.tech/
|
|
7
|
-
rdfs: "https://www.w3.org/2000/01/rdf-schema#"
|
|
8
|
-
rdf: https://www.w3.org/1999/02/22-rdf-syntax-ns#
|
|
9
|
-
|
|
10
|
-
$included:
|
|
11
|
-
- $id: python://iolanta.facets.cli.Default
|
|
12
|
-
rdfs:label: For the CLI, display a link to the node or (at least) its human readable label.
|
|
13
|
-
|
|
14
|
-
- $id: iolanta:cli
|
|
15
|
-
$type: iolanta:OutputDatatype
|
|
16
|
-
iolanta:hasDefaultFacet:
|
|
17
|
-
$id: python://iolanta.facets.cli.Record
|
|
18
|
-
rdfs:label: Show properties of a node
|
|
19
|
-
|
|
20
|
-
- $id: https://iolanta.tech/cli/record/title
|
|
21
|
-
iolanta:hasDefaultFacet:
|
|
22
|
-
$id: python://iolanta.facets.cli.Default
|
|
23
|
-
|
|
24
|
-
- $id: https://iolanta.tech/cli/record/property
|
|
25
|
-
iolanta:hasDefaultFacet:
|
|
26
|
-
$id: python://iolanta.facets.cli.Default
|
|
27
|
-
|
|
28
|
-
- $id: https://iolanta.tech/cli/record/value
|
|
29
|
-
iolanta:hasDefaultFacet:
|
|
30
|
-
$id: python://iolanta.facets.cli.Default
|
iolanta/data/html.yaml
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"@context":
|
|
2
|
-
"@import": https://json-ld.org/contexts/dollar-convenience.jsonld
|
|
3
|
-
vann: https://purl.org/vocab/vann/
|
|
4
|
-
foaf: https://xmlns.com/foaf/0.1/
|
|
5
|
-
owl: https://www.w3.org/2002/07/owl#
|
|
6
|
-
iolanta: https://iolanta.tech/
|
|
7
|
-
rdfs: "https://www.w3.org/2000/01/rdf-schema#"
|
|
8
|
-
rdf: https://www.w3.org/1999/02/22-rdf-syntax-ns#
|
|
9
|
-
|
|
10
|
-
$included:
|
|
11
|
-
- $id: iolanta:html
|
|
12
|
-
$type: iolanta:OutputDatatype
|
|
13
|
-
owl:sameAs:
|
|
14
|
-
$id: https://html.spec.whatwg.org/
|
|
15
|
-
iolanta:hasDefaultFacet: python://iolanta.facets.html.Default
|
|
File without changes
|