iolanta 2.1.6__py3-none-any.whl → 2.1.8__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.
Files changed (33) hide show
  1. iolanta/cli/main.py +1 -1
  2. iolanta/data/context.yaml +7 -7
  3. iolanta/data/iolanta.yaml +2 -2
  4. iolanta/data/textual-browser.yaml +7 -27
  5. iolanta/declension/data/declension.yamlld +7 -7
  6. iolanta/facets/facet.py +4 -0
  7. iolanta/facets/mkdocs_material_insiders_markdown/__init__.py +6 -0
  8. iolanta/facets/mkdocs_material_insiders_markdown/data/mkdocs_material_insiders_markdown.yamlld +20 -0
  9. iolanta/facets/mkdocs_material_insiders_markdown/facet.py +86 -0
  10. iolanta/facets/mkdocs_material_insiders_markdown/templates/datatype.jinja2.md +24 -0
  11. iolanta/facets/textual_browser/location.py +3 -1
  12. iolanta/facets/textual_browser/page_switcher.py +10 -0
  13. iolanta/facets/textual_class/facets.py +3 -0
  14. iolanta/facets/textual_class/textual-class.yamlld +25 -0
  15. iolanta/facets/textual_default/facets.py +50 -15
  16. iolanta/facets/textual_default/sparql/inverse-properties.sparql +1 -1
  17. iolanta/facets/textual_default/sparql/properties.sparql +2 -2
  18. iolanta/facets/textual_default/widgets.py +14 -6
  19. iolanta/iolanta.py +4 -41
  20. iolanta/labeled_triple_set/data/labeled_triple_set.yamlld +7 -7
  21. iolanta/mcp/cli.py +3 -2
  22. iolanta/models.py +0 -3
  23. iolanta/namespaces.py +2 -2
  24. iolanta/parse_quads.py +2 -2
  25. iolanta/sparqlspace/inference/wikidata-prop-label.sparql +10 -0
  26. iolanta/sparqlspace/inference/wikidata-statement-label.sparql +27 -0
  27. iolanta/sparqlspace/processor.py +78 -76
  28. {iolanta-2.1.6.dist-info → iolanta-2.1.8.dist-info}/METADATA +4 -3
  29. {iolanta-2.1.6.dist-info → iolanta-2.1.8.dist-info}/RECORD +31 -26
  30. {iolanta-2.1.6.dist-info → iolanta-2.1.8.dist-info}/WHEEL +1 -1
  31. {iolanta-2.1.6.dist-info → iolanta-2.1.8.dist-info}/entry_points.txt +1 -1
  32. iolanta/sparqlspace/inference/wikibase-claim.sparql +0 -9
  33. iolanta/sparqlspace/inference/wikibase-statement-property.sparql +0 -9
iolanta/cli/main.py CHANGED
@@ -11,7 +11,7 @@ from rdflib import Literal, URIRef
11
11
  from rich.console import Console
12
12
  from rich.markdown import Markdown
13
13
  from rich.table import Table
14
- from typer import Argument, Exit, Option, Typer, Context
14
+ from typer import Argument, Context, Exit, Option, Typer
15
15
  from yarl import URL
16
16
 
17
17
  from iolanta.cli.models import LogLevel
iolanta/data/context.yaml CHANGED
@@ -1,13 +1,13 @@
1
1
  "@context":
2
- rdfs: "https://www.w3.org/2000/01/rdf-schema#"
3
- rdf: https://www.w3.org/1999/02/22-rdf-syntax-ns#
2
+ rdfs: "http://www.w3.org/2000/01/rdf-schema#"
3
+ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
4
4
  iolanta: https://iolanta.tech/
5
- owl: https://www.w3.org/2002/07/owl#
5
+ owl: http://www.w3.org/2002/07/owl#
6
6
  schema: https://schema.org/
7
- xsd: https://www.w3.org/2001/XMLSchema#
8
- skos: https://www.w3.org/2004/02/skos/core#
9
- foaf: https://xmlns.com/foaf/0.1/
10
- vann: https://purl.org/vocab/vann/
7
+ xsd: http://www.w3.org/2001/XMLSchema#
8
+ skos: http://www.w3.org/2004/02/skos/core#
9
+ foaf: http://xmlns.com/foaf/0.1/
10
+ vann: http://purl.org/vocab/vann/
11
11
 
12
12
  "@vocab": "local:"
13
13
  "@base": "local:"
iolanta/data/iolanta.yaml CHANGED
@@ -4,8 +4,8 @@
4
4
  foaf: https://xmlns.com/foaf/0.1/
5
5
  owl: https://www.w3.org/2002/07/owl#
6
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#
7
+ rdfs: "http://www.w3.org/2000/01/rdf-schema#"
8
+ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
9
9
 
10
10
  $included:
11
11
  - $id: iolanta:Facet
@@ -1,14 +1,14 @@
1
1
  "@context":
2
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#
3
+ vann: http://purl.org/vocab/vann/
4
+ foaf: http://xmlns.com/foaf/0.1/
5
+ owl: http://www.w3.org/2002/07/owl#
6
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#
7
+ rdfs: "http://www.w3.org/2000/01/rdf-schema#"
8
+ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
9
9
  np: https://www.nanopub.org/nschema#
10
- dcterms: https://purl.org/dc/terms/
11
- xsd: https://www.w3.org/2001/XMLSchema#
10
+ dcterms: http://purl.org/dc/terms/
11
+ xsd: http://www.w3.org/2001/XMLSchema#
12
12
 
13
13
  iolanta:outputs:
14
14
  "@type": "@id"
@@ -88,21 +88,6 @@
88
88
  iolanta:hasDefaultFacet:
89
89
  $id: pkg:pypi/iolanta#textual-link
90
90
 
91
- - $id: https://wikiba.se/ontology#Statement
92
- iolanta:hasInstanceFacet:
93
- $id: pkg:pypi/iolanta#wikibase-statement-title
94
- $: Title
95
- →: https://iolanta.tech/datatypes/title
96
-
97
- - $id: rdfs:Class
98
- iolanta:hasInstanceFacet:
99
- $id: pkg:pypi/iolanta#textual-class
100
- $: Instances
101
- →: https://iolanta.tech/cli/textual
102
- ⪯:
103
- - pkg:pypi/iolanta#textual-browser
104
- - pkg:pypi/iolanta#textual-inverse-properties
105
-
106
91
  - $id: owl:Ontology
107
92
  iolanta:hasInstanceFacet:
108
93
  $id: pkg:pypi/iolanta#textual-ontology
@@ -125,10 +110,5 @@
125
110
  →: https://iolanta.tech/cli/textual
126
111
  ↦: ASK WHERE { ?subject $this ?object }
127
112
 
128
- - $id: pkg:pypi/iolanta#textual-class
129
- ↦:
130
- - ASK WHERE { ?instance a $this }
131
- - ASK WHERE { $this rdfs:subClassOf ?superclass }
132
-
133
113
  - $id: https://iolanta.tech/cli/textual
134
114
  iolanta:when-no-facet-found: pkg:pypi/iolanta#textual-no-facet-found
@@ -1,13 +1,13 @@
1
1
  "@context":
2
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#
3
+ vann: http://purl.org/vocab/vann/
4
+ foaf: http://xmlns.com/foaf/0.1/
5
+ owl: http://www.w3.org/2002/07/owl#
6
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/
7
+ rdfs: "http://www.w3.org/2000/01/rdf-schema#"
8
+ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
9
+ dcterms: http://purl.org/dc/terms/
10
+ dcam: http://purl.org/dc/dcam/
11
11
 
12
12
  iolanta:outputs:
13
13
  "@type": "@id"
iolanta/facets/facet.py CHANGED
@@ -20,6 +20,10 @@ class Facet(Generic[FacetOutput]):
20
20
  iolanta: 'iolanta.Iolanta' = field(repr=False)
21
21
  as_datatype: Optional[NotLiteralNode] = None
22
22
 
23
+ def __post_init__(self):
24
+ if type(self.this) == str:
25
+ raise ValueError(f'Facet {self.__class__.__name__} received a string as this: {self.this}')
26
+
23
27
  @property
24
28
  def stored_queries_path(self) -> Path:
25
29
  """Construct directory for stored queries for this facet."""
@@ -0,0 +1,6 @@
1
+ from iolanta.facets.mkdocs_material_insiders_markdown.facet import (
2
+ MkDocsMaterialInsidersMarkdownFacet,
3
+ )
4
+
5
+ __all__ = ['MkDocsMaterialInsidersMarkdownFacet']
6
+
@@ -0,0 +1,20 @@
1
+ "@context":
2
+ "@import": https://json-ld.org/contexts/dollar-convenience.jsonld
3
+ rdfs: "http://www.w3.org/2000/01/rdf-schema#"
4
+ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
5
+ iolanta: https://iolanta.tech/
6
+
7
+ $: rdfs:label
8
+
9
+ →:
10
+ "@type": "@id"
11
+ "@id": iolanta:outputs
12
+
13
+ ↦: iolanta:matches
14
+
15
+ $id: pkg:pypi/iolanta#mkdocs-material-insiders-markdown-datatype
16
+ $: OutputDatatype (mkdocs-material-insiders-markdown)
17
+
18
+ →: https://iolanta.tech/datatypes/mkdocs-material-insiders-markdown
19
+
20
+ ↦: ASK WHERE { $this a iolanta:OutputDatatype }
@@ -0,0 +1,86 @@
1
+ from pathlib import Path
2
+
3
+ from jinja2 import Environment, FileSystemLoader
4
+ from rdflib import URIRef
5
+
6
+ from iolanta.facets.facet import Facet
7
+ from iolanta.namespaces import DATATYPES, IOLANTA, RDF, RDFS
8
+
9
+
10
+ class MkDocsMaterialInsidersMarkdownFacet(Facet[str]):
11
+ """Render rdfs:Datatype nodes as mkdocs-material-insiders-markdown."""
12
+
13
+ META = Path(__file__).parent / 'data' / 'mkdocs_material_insiders_markdown.yamlld'
14
+ """Render rdfs:Datatype nodes as mkdocs-material-insiders-markdown."""
15
+
16
+ @property
17
+ def _template_env(self) -> Environment:
18
+ """Jinja2 template environment."""
19
+ template_path = Path(__file__).parent / 'templates'
20
+ return Environment(
21
+ loader=FileSystemLoader(str(template_path)),
22
+ autoescape=False,
23
+ )
24
+
25
+ def show(self) -> str:
26
+ """Render the datatype as markdown."""
27
+ # Get the label using title facet
28
+ label = self.render(
29
+ self.this,
30
+ as_datatype=DATATYPES.title,
31
+ )
32
+
33
+ # Get the comment/description
34
+ comment_rows = self.query(
35
+ """
36
+ SELECT ?comment WHERE {
37
+ $this rdfs:comment ?comment .
38
+ }
39
+ LIMIT 1
40
+ """,
41
+ this=self.this,
42
+ )
43
+ comment = str(comment_rows[0]['comment']) if comment_rows else None
44
+
45
+ # Get all types (rdf:type)
46
+ type_rows = self.query(
47
+ """
48
+ SELECT ?type WHERE {
49
+ $this rdf:type ?type .
50
+ }
51
+ """,
52
+ this=self.this,
53
+ )
54
+ types = [
55
+ {
56
+ 'uri': row['type'],
57
+ 'title': self.render(row['type'], as_datatype=DATATYPES.title),
58
+ }
59
+ for row in type_rows
60
+ ]
61
+
62
+ # Get all superclasses (rdfs:subClassOf)
63
+ superclass_rows = self.query(
64
+ """
65
+ SELECT ?superclass WHERE {
66
+ $this rdfs:subClassOf ?superclass .
67
+ }
68
+ """,
69
+ this=self.this,
70
+ )
71
+ superclasses = [
72
+ {
73
+ 'uri': row['superclass'],
74
+ 'title': self.render(row['superclass'], as_datatype=DATATYPES.title),
75
+ }
76
+ for row in superclass_rows
77
+ ]
78
+
79
+ template = self._template_env.get_template('datatype.jinja2.md')
80
+ return template.render(
81
+ label=label,
82
+ comment=comment,
83
+ types=types,
84
+ superclasses=superclasses,
85
+ )
86
+
@@ -0,0 +1,24 @@
1
+ # {{ label }}
2
+
3
+ <table>
4
+ <thead>
5
+ </thead>
6
+ <tbody>
7
+ {% if types %}
8
+ <tr>
9
+ <td>∈ Instance Of</td>
10
+ <td>{% for type in types %}<code><a href="{{ type.uri }}">{{ type.title }}</a></code>{% if not loop.last %}, {% endif %}{% endfor %}</td>
11
+ </tr>
12
+ {% endif %}
13
+ {% if superclasses %}
14
+ <tr>
15
+ <td>⊊ Subclass Of</td>
16
+ <td>{% for superclass in superclasses %}<code><a href="{{ superclass.uri }}">{{ superclass.title }}</a></code>{% if not loop.last %}, {% endif %}{% endfor %}</td>
17
+ </tr>
18
+ {% endif %}
19
+ </tbody>
20
+ </table>
21
+
22
+ {% if comment %}
23
+ {{ comment | safe }}
24
+ {% endif %}
@@ -2,11 +2,13 @@ from dataclasses import dataclass
2
2
 
3
3
  from rdflib import URIRef
4
4
 
5
+ from iolanta.models import NotLiteralNode
6
+
5
7
 
6
8
  @dataclass
7
9
  class Location:
8
10
  """Unique ID and IRI associated with it."""
9
11
 
10
12
  page_id: str
11
- url: str
13
+ url: NotLiteralNode
12
14
  facet_iri: URIRef | None = None
@@ -217,6 +217,9 @@ class PageSwitcher(IolantaWidgetMixin, ContentSwitcher): # noqa: WPS214
217
217
 
218
218
  def action_reload(self):
219
219
  """Reset Iolanta graph and re-render current view."""
220
+ if self.history.current is None:
221
+ return
222
+
220
223
  self.iolanta.reset()
221
224
 
222
225
  self.run_worker(
@@ -271,6 +274,13 @@ class PageSwitcher(IolantaWidgetMixin, ContentSwitcher): # noqa: WPS214
271
274
  facet_iri: str | None = None,
272
275
  ):
273
276
  """Go to an IRI."""
277
+ # Convert string to URIRef if needed.
278
+ # This happens when called via Textual action strings (from keyboard bindings
279
+ # in page.py line 24), which serialize URIRefs to strings in f-strings.
280
+ # Direct calls (like line 77) pass URIRef objects directly.
281
+ if isinstance(this, str):
282
+ this = URIRef(this)
283
+
274
284
  self.run_worker(
275
285
  functools.partial(
276
286
  self.render_iri,
@@ -1,5 +1,6 @@
1
1
  import functools
2
2
  import itertools
3
+ from pathlib import Path
3
4
  from typing import ClassVar, Iterable
4
5
 
5
6
  import funcy
@@ -186,6 +187,8 @@ class InstancesBody(Vertical):
186
187
  class Class(Facet[Widget]):
187
188
  """Render instances of a class."""
188
189
 
190
+ META = Path(__file__).parent / 'textual-class.yamlld'
191
+
189
192
  def stream_instances(self) -> Iterable[NotLiteralNode]:
190
193
  """
191
194
  Query and stream class instances lazily.
@@ -0,0 +1,25 @@
1
+ "@context":
2
+ "@import": https://json-ld.org/contexts/dollar-convenience.jsonld
3
+ iolanta: https://iolanta.tech/
4
+ rdfs: "http://www.w3.org/2000/01/rdf-schema#"
5
+ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
6
+
7
+ $: rdfs:label
8
+ →:
9
+ "@type": "@id"
10
+ "@id": iolanta:outputs
11
+ ⪯:
12
+ "@type": "@id"
13
+ "@id": iolanta:is-preferred-over
14
+ ↦: iolanta:matches
15
+
16
+ $id: pkg:pypi/iolanta#textual-class
17
+
18
+ $: Instances
19
+ →: https://iolanta.tech/cli/textual
20
+
21
+ ⪯:
22
+ - pkg:pypi/iolanta#textual-browser
23
+ - pkg:pypi/iolanta#textual-inverse-properties
24
+
25
+ ↦: ASK WHERE { ?instance a $this }
@@ -4,7 +4,7 @@ from xml.dom import minidom # noqa: S408
4
4
  import funcy
5
5
  from rdflib.term import Literal, Node
6
6
  from rich.syntax import Syntax
7
- from textual.containers import VerticalScroll
7
+ from textual.containers import Vertical, VerticalScroll
8
8
  from textual.widget import Widget
9
9
  from textual.widgets import Static
10
10
 
@@ -22,17 +22,28 @@ from iolanta.models import NotLiteralNode
22
22
  from iolanta.namespaces import DC, RDFS, SDO
23
23
 
24
24
 
25
+ class Description(Static):
26
+ """Static widget with padding for descriptions."""
27
+
28
+ DEFAULT_CSS = """
29
+ Description {
30
+ padding: 1;
31
+ }
32
+ """
33
+
34
+
25
35
  class TextualDefaultFacet(Facet[Widget]): # noqa: WPS214
26
36
  """Default rendering engine."""
27
37
 
28
38
  query_file_name = 'properties.sparql'
39
+ properties_on_the_right = False
29
40
 
30
41
  @functools.cached_property
31
42
  def grouped_properties(self) -> dict[NotLiteralNode, list[Node]]:
32
43
  """Properties of current node & their values."""
33
44
  property_rows = self.stored_query(
34
45
  self.query_file_name,
35
- iri=self.this,
46
+ this=self.this,
36
47
  )
37
48
 
38
49
  property_pairs = [
@@ -75,10 +86,18 @@ class TextualDefaultFacet(Facet[Widget]): # noqa: WPS214
75
86
  for property_value in property_values
76
87
  ]
77
88
 
78
- yield PropertyRow(
79
- property_name,
80
- PropertyValues(*property_values),
81
- )
89
+ if self.properties_on_the_right:
90
+ # Property name on the right (last column), values on the left (first column)
91
+ yield PropertyRow(
92
+ PropertyValues(*property_values),
93
+ property_name,
94
+ )
95
+ else:
96
+ # Property name on the left (first column), values on the right (last column)
97
+ yield PropertyRow(
98
+ property_name,
99
+ PropertyValues(*property_values),
100
+ )
82
101
 
83
102
  @functools.cached_property
84
103
  def description(self) -> str | Syntax | None:
@@ -134,22 +153,38 @@ class TextualDefaultFacet(Facet[Widget]): # noqa: WPS214
134
153
 
135
154
  def show(self) -> Widget:
136
155
  """Render the content."""
137
- return VerticalScroll(
138
- PageTitle(self.this),
139
- Static(self.description or ''),
140
- self.properties,
141
- )
142
156
 
157
+ widgets = [PageTitle(self.this)]
158
+
159
+ if self.description:
160
+ widgets.append(Description(self.description))
161
+
162
+ widgets.append(self.properties)
163
+
164
+ return VerticalScroll(*widgets)
165
+
166
+
167
+ class PageFooter(PageTitle):
168
+ """Page footer."""
169
+
170
+ DEFAULT_CSS = """
171
+ PageFooter {
172
+ dock: bottom;
173
+ background: darkmagenta;
174
+ }
175
+ """
143
176
 
144
177
  class InverseProperties(TextualDefaultFacet):
145
178
  """Inverse properties view."""
146
179
 
147
180
  query_file_name = 'inverse-properties.sparql'
181
+ properties_on_the_right = True
148
182
 
149
183
  def show(self) -> Widget:
150
184
  """Render the content."""
151
- return VerticalScroll(
152
- PageTitle(self.this, extra='[i]& its inverse RDF properties[/i]'),
153
- Static(self.description or ''),
154
- self.properties,
185
+ return Vertical(
186
+ VerticalScroll(
187
+ self.properties,
188
+ ),
189
+ PageFooter(self.this, extra='[i]& its inverse RDF properties[/i]'),
155
190
  )
@@ -1,3 +1,3 @@
1
1
  SELECT ?object ?property WHERE {
2
- ?object ?property $iri .
2
+ ?object ?property $this .
3
3
  }
@@ -1,9 +1,9 @@
1
1
  SELECT ?property ?object WHERE {
2
- $iri ?property ?object .
2
+ $this ?property ?object .
3
3
 
4
4
  # Exclude non-informative tautologies.
5
5
  FILTER (!(
6
- ?object = $iri
6
+ ?object = $this
7
7
  && ?property IN (
8
8
  owl:sameAs,
9
9
  owl:equivalentClass,
@@ -25,6 +25,7 @@ class PropertyName(Widget, can_focus=True, inherit_bindings=False):
25
25
  width: 15%;
26
26
  height: auto;
27
27
  margin-right: 1;
28
+ text-style: bold;
28
29
  }
29
30
 
30
31
  PropertyName:hover {
@@ -60,7 +61,8 @@ class PropertyName(Widget, can_focus=True, inherit_bindings=False):
60
61
 
61
62
  def render_title(self):
62
63
  """Render title in a separate thread."""
63
- return self.app.iolanta.render(self.iri, DATATYPES.title)
64
+ title = self.app.iolanta.render(self.iri, DATATYPES.title)
65
+ return f'⤚{title}→'
64
66
 
65
67
  def render(self) -> RenderResult:
66
68
  """Render node title."""
@@ -147,6 +149,13 @@ class PropertyValue(Widget, can_focus=True, inherit_bindings=False):
147
149
  """Return the property IRI for compatibility."""
148
150
  return self.property_value
149
151
 
152
+ def render_title(self):
153
+ """Render title in a separate thread."""
154
+ return self.app.iolanta.render(
155
+ self.iri,
156
+ as_datatype=DATATYPES.title,
157
+ )
158
+
150
159
  def on_worker_state_changed(self, event: Worker.StateChanged):
151
160
  """Show the title after it has been rendered."""
152
161
  match event.state:
@@ -191,7 +200,9 @@ class PropertiesContainer(Vertical):
191
200
  DEFAULT_CSS = """
192
201
  PropertiesContainer {
193
202
  height: auto;
194
- }"""
203
+ padding: 1;
204
+ }
205
+ """
195
206
 
196
207
  def render_all_properties(self):
197
208
  """Render all property names & values."""
@@ -199,10 +210,7 @@ class PropertiesContainer(Vertical):
199
210
 
200
211
  widget: PropertyName | PropertyValue
201
212
  for widget in widgets:
202
- widget.renderable = self.app.iolanta.render(
203
- widget.iri,
204
- as_datatype=DATATYPES.title,
205
- )
213
+ widget.renderable = widget.render_title()
206
214
 
207
215
  def on_mount(self):
208
216
  """Initiate rendering in the background."""
iolanta/iolanta.py CHANGED
@@ -119,7 +119,7 @@ class Iolanta: # noqa: WPS214
119
119
  processor='sparqlspace',
120
120
  initBindings=kwargs,
121
121
  )
122
- except ParseException as err:
122
+ except SyntaxError as err:
123
123
  raise SPARQLParseException(
124
124
  error=err,
125
125
  query=query_text,
@@ -171,8 +171,9 @@ class Iolanta: # noqa: WPS214
171
171
  self.logger.error(f'{source} | {parser_not_found}')
172
172
  continue
173
173
  except YAMLLDError as yaml_ld_error:
174
- self.logger.error(f'{source} | {yaml_ld_error}')
175
- continue
174
+ # .add() only processes local files, so errors should be raised
175
+ self.logger.error(f'{source_file} | {yaml_ld_error}')
176
+ raise
176
177
  except ValueError as value_error:
177
178
  self.logger.error(f'{source} | {value_error}')
178
179
  continue
@@ -307,44 +308,6 @@ class Iolanta: # noqa: WPS214
307
308
  error=err,
308
309
  ) from err
309
310
 
310
- def render_all(
311
- self,
312
- node: Node,
313
- as_datatype: NotLiteralNode,
314
- ) -> Iterable[Any]:
315
- """Find all possible Iolanta facets for a node and render them."""
316
- choices = list(
317
- FacetFinder(
318
- iolanta=self,
319
- node=node,
320
- as_datatype=as_datatype,
321
- ).choices(),
322
- )
323
-
324
- pairs = [
325
- (self.facet_resolver[row['facet']], row['output_datatype'])
326
- for row in choices
327
- ]
328
-
329
- facet_instances = [
330
- facet_class(
331
- this=node,
332
- iolanta=self,
333
- as_datatype=output_datatype,
334
- )
335
- for facet_class, output_datatype in pairs
336
- ]
337
-
338
- for facet in facet_instances:
339
- try:
340
- yield facet.show()
341
- except Exception as err:
342
- raise FacetError(
343
- node=node,
344
- facet_iri=None,
345
- error=err,
346
- ) from err
347
-
348
311
  def node_as_qname(self, node: Node):
349
312
  """Render node as a QName if possible."""
350
313
  qname = node_to_qname(node, self.graph)
@@ -1,13 +1,13 @@
1
1
  "@context":
2
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#
3
+ vann: http://purl.org/vocab/vann/
4
+ foaf: http://xmlns.com/foaf/0.1/
5
+ owl: http://www.w3.org/2002/07/owl#
6
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/
7
+ rdfs: "http://www.w3.org/2000/01/rdf-schema#"
8
+ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
9
+ dcterms: http://purl.org/dc/terms/
10
+ dcam: http://purl.org/dc/dcam/
11
11
 
12
12
  iolanta:outputs:
13
13
  "@type": "@id"
iolanta/mcp/cli.py CHANGED
@@ -1,6 +1,7 @@
1
+ from pathlib import Path
1
2
  from typing import Annotated
3
+
2
4
  from fastmcp import FastMCP
3
- from pathlib import Path
4
5
 
5
6
  from iolanta.cli.main import render_and_return
6
7
 
@@ -10,7 +11,7 @@ mcp = FastMCP("Iolanta MCP Server")
10
11
  @mcp.tool()
11
12
  def render_uri(
12
13
  uri: Annotated[str, 'URL, or file system path, to render'],
13
- as_format: Annotated[str, 'Format to render as. Examples: `labeled-triple-set`, `mermaid`']
14
+ as_format: Annotated[str, 'Format to render as. Examples: `labeled-triple-set`, `mermaid`'],
14
15
  ) -> str:
15
16
  """Render a URI."""
16
17
  result = render_and_return(uri, as_format)
iolanta/models.py CHANGED
@@ -83,9 +83,6 @@ class TripleTemplate(NamedTuple):
83
83
 
84
84
 
85
85
  def _normalize_term(term: Node):
86
- if isinstance(term, URIRef) and term.startswith('http://'):
87
- return URIRef(re.sub('^http', 'https', term))
88
-
89
86
  return term
90
87
 
91
88
 
iolanta/namespaces.py CHANGED
@@ -20,11 +20,11 @@ class OWL(rdflib.OWL):
20
20
 
21
21
 
22
22
  class RDFS(rdflib.RDFS):
23
- _NS = rdflib.Namespace('https://www.w3.org/2000/01/rdf-schema#')
23
+ _NS = rdflib.Namespace('http://www.w3.org/2000/01/rdf-schema#')
24
24
 
25
25
 
26
26
  class RDF(rdflib.RDF):
27
- _NS = rdflib.Namespace('https://www.w3.org/1999/02/22-rdf-syntax-ns#')
27
+ _NS = rdflib.Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
28
28
 
29
29
 
30
30
  class DCTERMS(rdflib.DCTERMS):
iolanta/parse_quads.py CHANGED
@@ -13,8 +13,8 @@ from iolanta.models import Quad
13
13
  from iolanta.namespaces import IOLANTA, META
14
14
 
15
15
  NORMALIZE_TERMS_MAP = MappingProxyType({
16
- URIRef(_url := 'https://www.w3.org/2002/07/owl'): URIRef(f'{_url}#'),
17
- URIRef(_url := 'https://www.w3.org/2000/01/rdf-schema'): URIRef(f'{_url}#'),
16
+ URIRef(_url := 'http://www.w3.org/2002/07/owl'): URIRef(f'{_url}#'),
17
+ URIRef(_url := 'http://www.w3.org/2000/01/rdf-schema'): URIRef(f'{_url}#'),
18
18
  })
19
19
 
20
20
 
@@ -0,0 +1,10 @@
1
+ PREFIX wikibase: <http://wikiba.se/ontology#>
2
+
3
+ CONSTRUCT {
4
+ ?thing rdfs:label ?label .
5
+ }
6
+ WHERE {
7
+ ?entity
8
+ (wikibase:claim | wikibase:qualifier | wikibase:statementProperty | wikibase:statementValueNormalized) ?thing ;
9
+ rdfs:label ?label .
10
+ }
@@ -0,0 +1,27 @@
1
+ PREFIX wikibase: <http://wikiba.se/ontology#>
2
+ PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
3
+ PREFIX prov: <http://www.w3.org/ns/prov#>
4
+
5
+ CONSTRUCT {
6
+ ?statement rdfs:label ?label .
7
+ }
8
+ WHERE {
9
+ ?statement a wikibase:Statement .
10
+
11
+ # Find predicates from statement to values (excluding metadata predicates)
12
+ ?statement ?statementProp ?value .
13
+
14
+ FILTER(?statementProp != wikibase:rank)
15
+ FILTER(?statementProp != rdf:type)
16
+ FILTER(?statementProp != prov:wasDerivedFrom)
17
+ FILTER(?statementProp != iolanta:last-loaded-time)
18
+
19
+ # Handle entity values: get their label
20
+ {
21
+ ?value rdfs:label ?label .
22
+ } UNION {
23
+ # Handle literal values: use the literal directly
24
+ ?statement ?statementProp ?label .
25
+ FILTER(isLiteral(?label))
26
+ }
27
+ }
@@ -35,6 +35,7 @@ from iolanta.namespaces import ( # noqa: WPS235
35
35
  DCTERMS,
36
36
  FOAF,
37
37
  IOLANTA,
38
+ LOCAL,
38
39
  META,
39
40
  OWL,
40
41
  PROV,
@@ -46,6 +47,8 @@ from iolanta.parse_quads import NORMALIZE_TERMS_MAP, parse_quads
46
47
 
47
48
  REASONING_ENABLED = True
48
49
  OWL_REASONING_ENABLED = False
50
+
51
+ INFERENCE_DIR = Path(__file__).parent / 'inference'
49
52
  INDICES = [
50
53
  URIRef('https://iolanta.tech/visualizations/index.yaml'),
51
54
  ]
@@ -112,7 +115,7 @@ def _extract_from_mapping( # noqa: WPS213
112
115
  algebra: Mapping[str, Any],
113
116
  ) -> Iterable[URIRef | Variable]:
114
117
  match algebra.name:
115
- case 'SelectQuery' | 'AskQuery' | 'Project' | 'Distinct':
118
+ case 'SelectQuery' | 'AskQuery' | 'Project' | 'Distinct' | 'Slice':
116
119
  yield from extract_mentioned_urls(algebra['p'])
117
120
 
118
121
  case 'BGP':
@@ -203,9 +206,6 @@ def normalize_term(term: Node) -> Node:
203
206
  * A dirty hack;
204
207
  * Based on hard code.
205
208
  """
206
- if isinstance(term, URIRef) and term.startswith('http://'):
207
- term = URIRef(re.sub('^http', 'https', term))
208
-
209
209
  return NORMALIZE_TERMS_MAP.get(term, term)
210
210
 
211
211
 
@@ -263,6 +263,12 @@ def _extract_nanopublication_uris(
263
263
  match algebra.name:
264
264
  case 'SelectQuery' | 'AskQuery' | 'Project' | 'Distinct' | 'Graph':
265
265
  yield from _extract_nanopublication_uris(algebra['p'])
266
+ case 'ConstructQuery':
267
+ # CONSTRUCT queries don't have nanopublication URIs in bindings
268
+ return
269
+
270
+ case 'Slice':
271
+ yield from _extract_nanopublication_uris(algebra['p'])
266
272
 
267
273
  case 'BGP':
268
274
  for retractor, retracts, retractee in algebra['triples']:
@@ -404,64 +410,6 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
404
410
  self.graph.last_not_inferred_source = None
405
411
  self.graph._indices_loaded = False
406
412
 
407
- def _infer_with_sparql(self):
408
- """
409
- Infer triples with SPARQL rules.
410
-
411
- FIXME:
412
- * Code these rules into SHACL or some other RDF based syntax;
413
- * Make them available at iolanta.tech/visualizations/ and indexed.
414
- """
415
- inference = Path(__file__).parent / 'inference'
416
-
417
- file_names = {
418
- 'wikibase-claim.sparql': URIRef('local:inference-wikibase-claim'),
419
- 'wikibase-statement-property.sparql': URIRef(
420
- 'local:inference-statement-property',
421
- ),
422
- }
423
-
424
- for file_name, graph_name in file_names.items():
425
- start_time = time.time()
426
- self.graph.update(
427
- update_object=(inference / file_name).read_text(),
428
- )
429
- triple_count = len(self.graph.get_context(graph_name))
430
- duration = datetime.timedelta(seconds=time.time() - start_time)
431
- self.logger.info(
432
- f'{file_name}: {triple_count} triple(s), '
433
- f'inferred at {duration}',
434
- )
435
-
436
- def maybe_apply_inference(self):
437
- """Apply global OWL RL inference if necessary."""
438
- if not REASONING_ENABLED:
439
- return
440
-
441
- if self.graph.last_not_inferred_source is None:
442
- return
443
-
444
- with self.inference_lock:
445
- self._infer_with_sparql()
446
- self._infer_with_owl_rl()
447
- self.logger.info('Inference @ cyberspace: complete.')
448
-
449
- self.graph.last_not_inferred_source = None
450
-
451
- def _infer_with_owl_rl(self):
452
- if not OWL_REASONING_ENABLED:
453
- return
454
-
455
- reasoner = reasonable.PyReasoner()
456
- reasoner.from_graph(self.graph)
457
- inferred_triples = reasoner.reason()
458
- inference_graph_name = BNode('_:inference')
459
- inferred_quads = [
460
- (*triple, inference_graph_name)
461
- for triple in inferred_triples
462
- ]
463
- self.graph.addN(inferred_quads)
464
-
465
413
  def _maybe_load_indices(self):
466
414
  if not self.graph._indices_loaded:
467
415
  for index in INDICES:
@@ -486,7 +434,7 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
486
434
 
487
435
  initBindings = initBindings or {}
488
436
  initNs = initNs or {}
489
-
437
+
490
438
  if isinstance(strOrQuery, Query):
491
439
  query = strOrQuery
492
440
 
@@ -494,12 +442,14 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
494
442
  parse_tree = parseQuery(strOrQuery)
495
443
  query = translateQuery(parse_tree, base, initNs)
496
444
 
497
- self.load_retracting_nanopublications_by_query(
498
- query=query,
499
- bindings=initBindings,
500
- base=base,
501
- namespaces=initNs,
502
- )
445
+ # Only extract nanopublications from SELECT/ASK queries, not CONSTRUCT
446
+ if query.algebra.name != 'ConstructQuery':
447
+ self.load_retracting_nanopublications_by_query(
448
+ query=query,
449
+ bindings=initBindings,
450
+ base=base,
451
+ namespaces=initNs,
452
+ )
503
453
 
504
454
  query, urls = extract_mentioned_urls_from_query(
505
455
  query=query,
@@ -508,15 +458,24 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
508
458
  namespaces=initNs,
509
459
  )
510
460
 
461
+ # Filter out inference graph names (they're not URLs to load)
462
+ urls = {url for url in urls if not str(url).startswith('inference:')}
463
+
511
464
  for url in urls:
512
465
  try:
513
466
  self.load(url)
514
467
  except Exception as err:
515
468
  self.logger.exception(f'Failed to load {url}: {err}', url, err)
516
469
 
517
- NanopubQueryPlugin(graph=self.graph)(query, bindings=initBindings)
470
+ # Run inference if there's new data since last inference run
471
+ # (after URLs are loaded so inference can use the loaded data)
472
+ if self.graph.last_not_inferred_source is not None:
473
+ self.logger.debug(f'Running inference, last_not_inferred_source: {self.graph.last_not_inferred_source}')
474
+ self._run_inference()
475
+ else:
476
+ self.logger.debug('Skipping inference, last_not_inferred_source is None')
518
477
 
519
- self.maybe_apply_inference()
478
+ NanopubQueryPlugin(graph=self.graph)(query, bindings=initBindings)
520
479
 
521
480
  is_anything_loaded = True
522
481
  while is_anything_loaded:
@@ -531,6 +490,7 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
531
490
  return query_result
532
491
 
533
492
  for row in bindings:
493
+ break
534
494
  for _, maybe_iri in row.items():
535
495
  if (
536
496
  isinstance(maybe_iri, URIRef)
@@ -566,12 +526,10 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
566
526
 
567
527
  def _follow_is_visualized_with_links(self, uri: URIRef):
568
528
  """Follow `dcterms:isReferencedBy` links."""
569
- self.logger.info(f'Following links for {uri}…')
570
529
  triples = self.graph.triples((uri, DCTERMS.isReferencedBy, None))
571
530
  for _, _, visualization in triples:
572
531
  if isinstance(visualization, URIRef):
573
532
  self.load(visualization)
574
- self.logger.info('Links followed.')
575
533
 
576
534
  def load( # noqa: C901, WPS210, WPS212, WPS213, WPS231
577
535
  self,
@@ -611,8 +569,6 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
611
569
  source_uri = normalize_term(source)
612
570
  if self._is_loaded(source_uri):
613
571
  return Skipped()
614
- else:
615
- self.logger.info(f'{source_uri} is not loaded yet')
616
572
 
617
573
  # FIXME This is definitely inefficient. However, python-yaml-ld caches
618
574
  # the document, so the performance overhead is not super high.
@@ -718,8 +674,9 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
718
674
  for quad in quads
719
675
  })
720
676
  self.logger.info(
721
- f'{source} | loaded successfully into graphs: {into_graphs}',
677
+ f'{source} | loaded {len(quads)} triples into graphs: {into_graphs}',
722
678
  )
679
+
723
680
  return Loaded()
724
681
 
725
682
  def resolve_term(self, term: Node, bindings: dict[str, Node]):
@@ -732,6 +689,51 @@ class GlobalSPARQLProcessor(Processor): # noqa: WPS338, WPS214
732
689
 
733
690
  return term
734
691
 
692
+ def _run_inference(self): # noqa: WPS231
693
+ """
694
+ Run inference queries from the inference directory.
695
+
696
+ For each SPARQL file in the inference directory:
697
+ 1. Truncate the named graph `local:inference-{filename}`
698
+ 2. Execute the CONSTRUCT query
699
+ 3. Insert the resulting triples into that graph
700
+ """
701
+ with self.inference_lock:
702
+ for inference_file in INFERENCE_DIR.glob('*.sparql'):
703
+ filename = inference_file.stem # filename without .sparql extension
704
+ inference_graph = URIRef(f'inference:{filename}')
705
+
706
+ # Truncate the inference graph
707
+ context = self.graph.get_context(inference_graph)
708
+ context.remove((None, None, None))
709
+
710
+ # Read and execute the CONSTRUCT query
711
+ query_text = inference_file.read_text()
712
+ result = self.graph.query(query_text)
713
+
714
+ # CONSTRUCT queries return a SPARQLResult with a graph attribute
715
+ result_graph = result.get('graph') if isinstance(result, dict) else result.graph
716
+ self.logger.debug(f'Inference {filename}: result_graph is {result_graph}, type: {type(result_graph)}')
717
+ if result_graph is not None:
718
+ inferred_quads = [
719
+ (s, p, o, inference_graph)
720
+ for s, p, o in result_graph
721
+ ]
722
+ self.logger.debug(f'Inference {filename}: generated {len(inferred_quads)} quads')
723
+
724
+ if inferred_quads:
725
+ self.graph.addN(inferred_quads)
726
+ self.logger.info(
727
+ 'Inference {filename}: added {count} triples',
728
+ filename=filename,
729
+ count=len(inferred_quads),
730
+ )
731
+ else:
732
+ self.logger.debug(f'Inference {filename}: result_graph is None')
733
+
734
+ # Clear the flag after running inference
735
+ self.graph.last_not_inferred_source = None
736
+
735
737
  def load_retracting_nanopublications_by_query( # noqa: WPS231
736
738
  self,
737
739
  query: Query,
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: iolanta
3
- Version: 2.1.6
3
+ Version: 2.1.8
4
4
  Summary: Semantic Web browser
5
5
  License: MIT
6
6
  Author: Anatoly Scherbakov
@@ -10,6 +10,7 @@ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
13
14
  Provides-Extra: all
14
15
  Requires-Dist: boltons (>=24.0.0)
15
16
  Requires-Dist: classes (>=0.4.0)
@@ -33,7 +34,7 @@ Requires-Dist: rich (>=13.3.1)
33
34
  Requires-Dist: textual (>=0.83.0)
34
35
  Requires-Dist: typer (>=0.9.0)
35
36
  Requires-Dist: watchfiles (>=1.0.4)
36
- Requires-Dist: yaml-ld (>=1.1.14,<2.0.0)
37
+ Requires-Dist: yaml-ld (>=1.1.15)
37
38
  Requires-Dist: yarl (>=1.9.4)
38
39
  Description-Content-Type: text/markdown
39
40
 
@@ -6,17 +6,17 @@ 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=_fJ9XAwANaYvwZ9ige4PafaB4Mx-InYibEXXExUFrZ8,4524
9
+ iolanta/cli/main.py,sha256=CZ22-DQY3xHvvUiq1o9XDrpC2an4429yQNOE8LytDkg,4524
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/context.yaml,sha256=LUBasiBKgQeGAYjYV_T5XvgPlrdzACeKaZwY_rKzjtI,1636
14
+ iolanta/data/context.yaml,sha256=U7qs7fb8YRJWTbvKuubsQ_lIltIbWJDEDjLoy53k_Ck,1629
15
15
  iolanta/data/graph-triples.yamlld,sha256=rtn-HfbijaqbmjCrKv-2pVV_aaJhB_9_OqXA_yLznCs,209
16
- iolanta/data/iolanta.yaml,sha256=xubIFBNU02lmFXhgOSuyQwUcZD3xCqVfeVAZMvOxKbI,1433
17
- iolanta/data/textual-browser.yaml,sha256=nJbDS0B3G-emM9vCu2DIlWZBhzmWbO3zrWkt_Rmv5uA,3700
16
+ iolanta/data/iolanta.yaml,sha256=eRb7f7Mvv8NN7Hq5NGIqLNvxFflqGae3IvG55F19OKE,1431
17
+ iolanta/data/textual-browser.yaml,sha256=arGPFQMtKK-LkWcDB50fC0jpztkS4T9B4prCDx_WTMo,3090
18
18
  iolanta/declension/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- iolanta/declension/data/declension.yamlld,sha256=fA1OptmhyIK7-mNy3fbK5G07OE4ueBW2x812b7VQkzA,975
19
+ iolanta/declension/data/declension.yamlld,sha256=xpHW458GS6Q2NQJxOzGxqeX-awvtlk6WjAAQUjBmiMw,968
20
20
  iolanta/declension/facet.py,sha256=7SsPACjeIGnONLhfjB4KBjqt-FRISVpZenshJzqC0A8,1349
21
21
  iolanta/declension/sparql/declension.sparql,sha256=T84bfCNDXodmFEH26S45DyOJHoWz3T4cC7hSwtAcFSw,185
22
22
  iolanta/ensure_is_context.py,sha256=9aok8asyEx7KPesOR28VBDb3Ch9kfc3eoCpQSJwj07U,717
@@ -30,7 +30,7 @@ iolanta/facets/cli/record.py,sha256=-nIhe6hXkoI5LJtZhqBoT8ZkYGxlLwMDbPc-xmwNhrE,
30
30
  iolanta/facets/cli/sparql/link.sparql,sha256=WtWEfLAvdGc2gP0IhZil6Vglkydc3VO24vk4GwRnR5I,163
31
31
  iolanta/facets/cli/sparql/record.sparql,sha256=GBWkmNelvaSDbvl7v0-LsJHdjFPbsSAW49kNFOUeoGQ,63
32
32
  iolanta/facets/errors.py,sha256=sEBmnzhFcRIgOT18hfNwyMMjry0waa6IB-jC2NVTA50,4124
33
- iolanta/facets/facet.py,sha256=hV_NzUEIiy25EENXd0qGRyJ1SCRuztVdy1ZFUK3Sc44,2070
33
+ iolanta/facets/facet.py,sha256=s9XmH-t0tkkVW2wwTdgv7mIW_gaW18rYVfnYgelkUtc,2239
34
34
  iolanta/facets/foaf_person_title/__init__.py,sha256=oj4MNVQvv8Dysb27xiWjtZCii8-nT7-WFa3WMWUwbtU,67
35
35
  iolanta/facets/foaf_person_title/facet.py,sha256=_lKtRosZWuKpjKZ3Ssdq79jVhiLE53jaf22Rnq4HCxs,634
36
36
  iolanta/facets/foaf_person_title/sparql/names.sparql,sha256=p_2hHZXhEaJ8IwGlvLoN0vb0vhGqo44uAVRpDyTzflU,107
@@ -46,6 +46,10 @@ iolanta/facets/html/default.py,sha256=Dmf_kYiL2M954iigyYsiWrMstwZ1nvxKetuR6aW3xY
46
46
  iolanta/facets/icon.py,sha256=Dubh9eCvb4kj-ppEJsTno_mn78og8YIRnDrmcefOsgs,495
47
47
  iolanta/facets/locator/sparql/get-query-to-facet.sparql,sha256=ZoXrclgZPy0FpPhVVMb6VnIXLIUgIc-Q7Pa5ySsNSSU,114
48
48
  iolanta/facets/locator.py,sha256=swDkDqo_N8GEPtkDRh-Tp7npyLuPUaZpZDHZPUc2Om4,9089
49
+ iolanta/facets/mkdocs_material_insiders_markdown/__init__.py,sha256=v5vnYeWJDYIZhXDbuKdhwjkq2Y13dPHNiQuBMAwzEgI,164
50
+ iolanta/facets/mkdocs_material_insiders_markdown/data/mkdocs_material_insiders_markdown.yamlld,sha256=_eTa2_Hqd-QqufBrKD8-wWv1XnKs3QCJar4ONGXH3Nk,549
51
+ iolanta/facets/mkdocs_material_insiders_markdown/facet.py,sha256=EY-H4gR5YgiDVt2pFbhXDuuDlAdo43vuwOvfhqFuRr4,2504
52
+ iolanta/facets/mkdocs_material_insiders_markdown/templates/datatype.jinja2.md,sha256=k9GSdy27mAY3eRL899pk6ZCYr4ZpEY1EuM5RY-OApYM,551
49
53
  iolanta/facets/page_title.py,sha256=TwgZK2g_e5UoWYjKNgDzzkmq1EI3cY58680iC8N9kZI,1407
50
54
  iolanta/facets/qname.py,sha256=Z2wjDWV90Z4vuwLj31MSf5EBGTb0dxzjlKl-Iv4dPao,533
51
55
  iolanta/facets/textual_browser/__init__.py,sha256=sKgDvXOwib9n9d63kdtKCEv26-FoL0VN6zxDmfcheZ8,104
@@ -53,23 +57,24 @@ iolanta/facets/textual_browser/app.py,sha256=rF34KbWi0L-Mozwzm-wzBS-3OqCcwbaXl0G
53
57
  iolanta/facets/textual_browser/facet.py,sha256=5mX1l6P-Ga7buzXmItxSpta6G_D4Fvwv8H6mU8-3g80,742
54
58
  iolanta/facets/textual_browser/history.py,sha256=b3jTwVkVe0ZBcYkHGJ_zKIV4MSMScDdabmLQIjOZfes,1087
55
59
  iolanta/facets/textual_browser/home.py,sha256=GfDD1G2HiwdLkPkNdcYRqVIxDl5tWH9fewh_FJb8G-I,384
56
- iolanta/facets/textual_browser/location.py,sha256=w0La8bVTpJiVo1_hFTDQeIUdDdqfhYnoihuZW-f130c,205
60
+ iolanta/facets/textual_browser/location.py,sha256=qWa7xUgaWKYOmiQuwI1TbyvujpKRb1pxJJZ8lFDcjKk,259
57
61
  iolanta/facets/textual_browser/models.py,sha256=DqTBjhkkTt5mNwqr4DzNbPSqzV-QtNqfKj7wpn6T3ao,173
58
62
  iolanta/facets/textual_browser/page.py,sha256=NkcQ5rSKZRbp63C8ozgsR_iVhcKHGv_SytUCQyGa7ss,786
59
- iolanta/facets/textual_browser/page_switcher.py,sha256=ryymmjN_q7Ukk-PY-Gh2XJozdBgkloHhIDPpW2KxWnI,9794
63
+ iolanta/facets/textual_browser/page_switcher.py,sha256=2hKDqygVRaf092V3X99gnGLIk044XKKKkA0sFttrc-I,10218
60
64
  iolanta/facets/textual_class/__init__.py,sha256=tiL0p-3JspGcBRj4qa3rmoBFAuadk71l2ja2lJN6CEs,75
61
- iolanta/facets/textual_class/facets.py,sha256=pz7NkT4_5wgMH0NIKxOoURQDk-qhl022OQWgbmtrC8w,6003
65
+ iolanta/facets/textual_class/facets.py,sha256=MiGTapgt30ME2fapwdrD_yQj4mhkdmyMAjzxaoLW5Dk,6087
62
66
  iolanta/facets/textual_class/sparql/instances.sparql,sha256=v0rHyr0Ha-ioZ2ssv0ytqeO8-Qt1xnX9FKfwSstttzI,230
67
+ iolanta/facets/textual_class/textual-class.yamlld,sha256=HBQCC3Kg41rFfPne9Gx1CXsQ3nQPXmwUXh9hr3AQU5A,586
63
68
  iolanta/facets/textual_default/__init__.py,sha256=snxA0FEY9qfAxNv3MlZLrJsXugD4dvs5hLStZWV7soM,158
64
- iolanta/facets/textual_default/facets.py,sha256=Cw5_T7p8G_TNqMhK05FQYE0Bra-Vr4ZdqMBBhLbsOe0,4743
65
- iolanta/facets/textual_default/sparql/inverse-properties.sparql,sha256=daHNdhmh92Q77CSf7ULbhxg57CuYsRFfMnXQz4VYEug,64
69
+ iolanta/facets/textual_default/facets.py,sha256=7rzF8jpu0TSX1uDTEUAKodST12Pd7bbDyDrt_Kk1u3g,5625
70
+ iolanta/facets/textual_default/sparql/inverse-properties.sparql,sha256=2m2q3C6jMQ_8o-0cPgIYrT77l6UnY3oqI37-Ed7p4-c,65
66
71
  iolanta/facets/textual_default/sparql/label.sparql,sha256=IWAkkgMKtIZ7Zpg8LUJ5fDZ9tiI8fiRYZo-zT61Fong,121
67
72
  iolanta/facets/textual_default/sparql/nodes-for-property.sparql,sha256=J9vg0Pz2HXDlPCeZ6IS2C0wODrpYDuNeD6DYT6UdNsU,68
68
- iolanta/facets/textual_default/sparql/properties.sparql,sha256=stDbvFP4g6YKYphpNRx4bBOi0rP0YYsaTrY1pqaiaik,332
73
+ iolanta/facets/textual_default/sparql/properties.sparql,sha256=fr33KZ4OiwR5PK5QVibcO5azkZYNC2Ollq2FXIb-TTc,334
69
74
  iolanta/facets/textual_default/tcss/default.tcss,sha256=v6k6LvZMndRW4t9Iq-7QF59U_LJTdohRsyavwTY5ruI,69
70
75
  iolanta/facets/textual_default/templates/default.md,sha256=CuD5lISsE2eAVnm2z6kfNff-vEgrNG95Wi5LTgkieWY,21
71
76
  iolanta/facets/textual_default/triple_uri_ref.py,sha256=XfuNPaAe-YxH8IyrdrHQ641aWh5zVMVs0L0WC3D6A4M,1279
72
- iolanta/facets/textual_default/widgets.py,sha256=w6wk_x72CANmZk9XHS7G0YOT3h3IoitKkrFBuyTI81o,9177
77
+ iolanta/facets/textual_default/widgets.py,sha256=uusHQzIERESBEyYI0k0bnYirLVZvFtsxikonh195bjw,9362
73
78
  iolanta/facets/textual_graph/__init__.py,sha256=DWd2gljzL8SiyYKQdBH78HouF1EMqgCH-w0K5OEmL2I,59
74
79
  iolanta/facets/textual_graph/facets.py,sha256=ed6zt7DnAlwWkOB2D3MDQrtKB3tNiegPpZbRKIoz5Zg,876
75
80
  iolanta/facets/textual_graph/sparql/triples.sparql,sha256=5rFVGcvZzEHZj2opQQp0YlxJLpEdl-r1RjkPwo8j7t0,145
@@ -102,13 +107,13 @@ iolanta/facets/title/sparql/title.sparql,sha256=VKHeE9NvV6sFgVGvsLQD9z__J4APj8TD
102
107
  iolanta/facets/wikibase_statement_title/__init__.py,sha256=_yk1akxgSJOiUBJIc8QGrD2vovvmx_iw_vJDuv1rD7M,91
103
108
  iolanta/facets/wikibase_statement_title/facets.py,sha256=gNRqiwOTMxyyHYvb_vIrfd-4ipb8wfyJ4GgPcQdyy9E,726
104
109
  iolanta/facets/wikibase_statement_title/sparql/statement-title.sparql,sha256=n07DQWxKqB5c3CA4kacq2HSN0R0dLgnMnLP1AxMo5YA,320
105
- iolanta/iolanta.py,sha256=ONIjejjidXfRIe9pVjCxEyI5Jo1yZdn-XGLuo73tcSw,11074
110
+ iolanta/iolanta.py,sha256=7w1Hcq8O1unNs6fmMqOD94kdLWd4XlRISEDiEukJ33c,10127
106
111
  iolanta/labeled_triple_set/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
- iolanta/labeled_triple_set/data/labeled_triple_set.yamlld,sha256=RqVhaXTIDA2Fip32E1TlH4cn_sDLC7H-NGZ5XpK-hio,1015
112
+ iolanta/labeled_triple_set/data/labeled_triple_set.yamlld,sha256=P3oAPSPsirpbcRXej-VekuYFTpWqrkysYsxghZc3bTk,1008
108
113
  iolanta/labeled_triple_set/labeled_triple_set.py,sha256=o4IgvTvPd0mzBtpgHYd4n1xpujYdAvWBr6gIYwp5vnA,4061
109
114
  iolanta/labeled_triple_set/sparql/triples.sparql,sha256=VsCmYN5AX7jSIiFm-SqLcRcOvUVj8yyZI4PSzKROtQw,82
110
115
  iolanta/mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
- iolanta/mcp/cli.py,sha256=vWlcvyzB54efTtFEZ7VEfwuePhOqQo0_kRplz3syaqM,1114
116
+ iolanta/mcp/cli.py,sha256=wmtJgeTijIoDe0C-3uGekVfYlhj2jqL30p_u_NuJzR0,1116
112
117
  iolanta/mcp/prompts/nanopublication_assertion_authoring_rules.md,sha256=Z5YL9YwcLHCqA4zQK2ziSQYgyR4SlhUO9LV_zHGf0JQ,2643
113
118
  iolanta/mcp/prompts/rules.md,sha256=LddpoNfUACfvWBNJ_ArAyJfP2zlQstlXS2QA6GCl9QI,4651
114
119
  iolanta/mermaid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -118,10 +123,10 @@ iolanta/mermaid/models.py,sha256=mlhtkqChpF-5z4Nt3s8a0j64cuT34BUKxLIYNS2RcIA,438
118
123
  iolanta/mermaid/sparql/ask-has-triples.sparql,sha256=mOYJ_rutEG_15PKTCHSv2GqzbkAawIn1U2kjkIr_Me0,41
119
124
  iolanta/mermaid/sparql/graph.sparql,sha256=mDGf05od3CUFhzI6rcqt5ZMVy-gSKDu-WxmV_zpIsVI,62
120
125
  iolanta/mermaid/sparql/subgraphs.sparql,sha256=VuoOYr_ZtKXXRrBpAEJek0mBRzR9EV-KnKENgAbkzCs,71
121
- iolanta/models.py,sha256=M-1dTxPwjTyUgQ4VCOiH6Q7ltNJiDPG0l1XQH430Omo,3250
122
- iolanta/namespaces.py,sha256=H_qYyxCqbIGMOczpT9vUBHumTW9QJ-Y6qXHbJgFwFP8,1210
126
+ iolanta/models.py,sha256=2VrJGQE1YXbbVB1K5McCXe2CLAlzOUhA8FvbRI10nCc,3131
127
+ iolanta/namespaces.py,sha256=NiCDzcvJHpnSH0kvEE94xyt0X9R-yqjkI_TEgj_3Pd4,1208
123
128
  iolanta/node_to_qname.py,sha256=a82_qpgT87cbekY_76tTkl4Z-6Rz6am4UGIQChUf9Y0,794
124
- iolanta/parse_quads.py,sha256=nKUGS_OVpCA6PFA5rOd6Qa5ik_eQhCL-Mlv1zDXk_Co,4541
129
+ iolanta/parse_quads.py,sha256=X-3hQAFzRD9U8KCuZMQTVOAapJR4OHkPoRbgJYiVbnk,4539
125
130
  iolanta/plugin.py,sha256=MSxpuOIx93AgBahfS8bYh31MEgcwtUSQhj4Js7fgdSI,1096
126
131
  iolanta/query_result.py,sha256=VLLBkewUEymtzfB0jeIeRE3Np6pAgo959RPgNsEmiq8,1545
127
132
  iolanta/reformat_blank_nodes.py,sha256=MAVcXusUioKzAoTEHAMume5Gt9vBEpxJGrngqFzmkJI,712
@@ -132,14 +137,14 @@ iolanta/resolvers/pypi.py,sha256=BJA7PGPaFFIxisCp-1-pjWZYNTDRVA5am5Nn8O2WddM,349
132
137
  iolanta/resolvers/python_import.py,sha256=hFs2Fi7BDjdKllgrGJhTaqjfcT7Vaj0j1b45CJt21B0,522
133
138
  iolanta/sparqlspace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
134
139
  iolanta/sparqlspace/cli.py,sha256=pb6q03lzrU8OaZ4A3QAQEmaFYcQ_-sHUrPhRs6GE88w,1590
135
- iolanta/sparqlspace/inference/wikibase-claim.sparql,sha256=JSawj3VTc9ZPoU9mvh1w1AMYb3cWyZ3x1rEYWUsKZ6A,209
136
- iolanta/sparqlspace/inference/wikibase-statement-property.sparql,sha256=SkSHZZlxWVDwBM3aLo0Q7hLuOj9BsIQnXtcuAuwaxqU,240
137
- iolanta/sparqlspace/processor.py,sha256=IkBPsijinHvryp0OnlE2OW6HLFbfFGhZuAQ-ytQTg8w,24149
140
+ iolanta/sparqlspace/inference/wikidata-prop-label.sparql,sha256=JYLAs28Z3a77cMcv44aZplwwrdqB-yshZn1dDZmRFAU,250
141
+ iolanta/sparqlspace/inference/wikidata-statement-label.sparql,sha256=_Dp9jKCpCp2pLk0uacNUhUvvQ2Hov-WiMFprtuYTRyY,759
142
+ iolanta/sparqlspace/processor.py,sha256=nEmVz7QjYtP33Gr8x7GVrX3DlDSGIRJJornnNCCEu9s,25041
138
143
  iolanta/sparqlspace/sparqlspace.py,sha256=Y8_ZPXwuGEXbEes6XQjaQWA2Zv9y8SWxMPDFdqVBGFo,796
139
144
  iolanta/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
145
  iolanta/widgets/description.py,sha256=98Qd3FwT9r8sYqKjl9ZEptaVX9jJ2ULWf0uy3j52p5o,800
141
146
  iolanta/widgets/mixin.py,sha256=nDRCOc-gizCf1a5DAcYs4hW8eZEd6pHBPFsfm0ncv7E,251
142
- iolanta-2.1.6.dist-info/METADATA,sha256=4v_M-GuBzoo1gqKMzlr3S9M9oy83i-RfA4U-qfcGHCg,2272
143
- iolanta-2.1.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
144
- iolanta-2.1.6.dist-info/entry_points.txt,sha256=TK9UQRFPXLEoOJOfyf9ywntoXHXSzkZyHV--lZ10eW0,1678
145
- iolanta-2.1.6.dist-info/RECORD,,
147
+ iolanta-2.1.8.dist-info/METADATA,sha256=2M-jlo8FVme0EFFKWO6cUr0yTvB4AnVFmO2d5uk8zMU,2316
148
+ iolanta-2.1.8.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
149
+ iolanta-2.1.8.dist-info/entry_points.txt,sha256=Vu0W4D6H74HsTICvD8CDB1wYs6XNSyu55EZVXMo4H84,1718
150
+ iolanta-2.1.8.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -8,6 +8,7 @@ boolean=iolanta.facets.generic:BoolLiteral
8
8
  icon=iolanta.facets.icon:IconFacet
9
9
  labeled-triple-set=iolanta.labeled_triple_set.labeled_triple_set:LabeledTripleSet
10
10
  mermaid-graph=iolanta.mermaid.facet:Mermaid
11
+ mkdocs-material-insiders-markdown-datatype=iolanta.facets.mkdocs_material_insiders_markdown:MkDocsMaterialInsidersMarkdownFacet
11
12
  qname=iolanta.facets.qname:QNameFacet
12
13
  rich-declension-table=iolanta.declension.facet:RichDeclensionTable
13
14
  textual-browser=iolanta.facets.textual_browser:TextualBrowserFacet
@@ -25,7 +26,6 @@ textual-property-pairs=iolanta.facets.textual_property_pairs_table:TextualProper
25
26
  textual-provenance=iolanta.facets.textual_provenance:TextualProvenanceFacet
26
27
  title=iolanta.facets.title:TitleFacet
27
28
  title-foaf-person=iolanta.facets.foaf_person_title:FOAFPersonTitle
28
- wikibase-statement-title=iolanta.facets.wikibase_statement_title:WikibaseStatementTitle
29
29
 
30
30
  [iolanta.plugins]
31
31
  base=iolanta:IolantaBase
@@ -1,9 +0,0 @@
1
- INSERT {
2
- GRAPH <local:inference-wikibase-claim> {
3
- ?prop <https://schema.org/name> ?name .
4
- }
5
- }
6
- WHERE {
7
- ?entity <https://wikiba.se/ontology#claim> ?prop .
8
- ?entity <https://schema.org/name> ?name .
9
- }
@@ -1,9 +0,0 @@
1
- INSERT {
2
- GRAPH <local:inference-wikibase-statement-property> {
3
- ?statement <https://schema.org/name> ?name .
4
- }
5
- }
6
- WHERE {
7
- ?prop <https://wikiba.se/ontology#statementProperty> ?statement .
8
- ?prop <https://schema.org/name> ?name .
9
- }