iolanta 2.1.12__py3-none-any.whl → 2.1.13__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 +200 -80
- iolanta/facets/query/__init__.py +0 -0
- iolanta/facets/query/ask_result_csv.py +23 -0
- iolanta/facets/query/ask_result_json.py +24 -0
- iolanta/facets/query/ask_result_table.py +23 -0
- iolanta/facets/query/construct_result_csv.py +34 -0
- iolanta/facets/query/construct_result_json.py +32 -0
- iolanta/facets/query/construct_result_table.py +55 -0
- iolanta/facets/query/data/query_result.yamlld +102 -0
- iolanta/facets/query/select_result_csv.py +36 -0
- iolanta/facets/query/select_result_json.py +24 -0
- iolanta/facets/query/select_result_table.py +48 -0
- iolanta/mcp/cli.py +16 -3
- iolanta/mermaid/models.py +19 -10
- {iolanta-2.1.12.dist-info → iolanta-2.1.13.dist-info}/METADATA +1 -1
- {iolanta-2.1.12.dist-info → iolanta-2.1.13.dist-info}/RECORD +18 -7
- {iolanta-2.1.12.dist-info → iolanta-2.1.13.dist-info}/WHEEL +1 -1
- {iolanta-2.1.12.dist-info → iolanta-2.1.13.dist-info}/entry_points.txt +9 -0
iolanta/cli/main.py
CHANGED
|
@@ -7,18 +7,25 @@ from typing import Annotated
|
|
|
7
7
|
import loguru
|
|
8
8
|
import platformdirs
|
|
9
9
|
from documented import DocumentedError
|
|
10
|
-
from rdflib import Literal, URIRef
|
|
10
|
+
from rdflib import Graph, 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,
|
|
14
|
+
from typer import Argument, Exit, Option, Typer
|
|
15
15
|
from yarl import URL
|
|
16
16
|
|
|
17
17
|
from iolanta.cli.models import LogLevel
|
|
18
|
+
from iolanta.facets.errors import FacetNotFound
|
|
18
19
|
from iolanta.iolanta import Iolanta
|
|
19
20
|
from iolanta.models import NotLiteralNode
|
|
21
|
+
from iolanta.namespaces import DATATYPES
|
|
22
|
+
from iolanta.query_result import (
|
|
23
|
+
QueryResult,
|
|
24
|
+
SPARQLParseException,
|
|
25
|
+
SelectResult,
|
|
26
|
+
)
|
|
20
27
|
|
|
21
|
-
DEFAULT_LANGUAGE = locale.getlocale()[0].split(
|
|
28
|
+
DEFAULT_LANGUAGE = locale.getlocale()[0].split("_")[0]
|
|
22
29
|
|
|
23
30
|
|
|
24
31
|
console = Console()
|
|
@@ -40,23 +47,18 @@ def string_to_node(name: str) -> NotLiteralNode:
|
|
|
40
47
|
return URIRef(name)
|
|
41
48
|
|
|
42
49
|
path = Path(name).absolute()
|
|
43
|
-
return URIRef(f
|
|
50
|
+
return URIRef(f"file://{path}")
|
|
44
51
|
|
|
45
52
|
|
|
46
53
|
def decode_datatype(datatype: str) -> URIRef:
|
|
47
|
-
if datatype.startswith(
|
|
54
|
+
if datatype.startswith("http"):
|
|
48
55
|
return URIRef(datatype)
|
|
49
56
|
|
|
50
|
-
return URIRef(f
|
|
57
|
+
return URIRef(f"https://iolanta.tech/datatypes/{datatype}")
|
|
51
58
|
|
|
52
59
|
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
as_datatype: str,
|
|
56
|
-
language: str = DEFAULT_LANGUAGE,
|
|
57
|
-
log_level: LogLevel = LogLevel.ERROR,
|
|
58
|
-
):
|
|
59
|
-
"""Render a given URL."""
|
|
60
|
+
def setup_logging(log_level: LogLevel):
|
|
61
|
+
"""Configure and return logger."""
|
|
60
62
|
level = {
|
|
61
63
|
LogLevel.DEBUG: logging.DEBUG,
|
|
62
64
|
LogLevel.INFO: logging.INFO,
|
|
@@ -64,111 +66,229 @@ def render_and_return(
|
|
|
64
66
|
LogLevel.ERROR: logging.ERROR,
|
|
65
67
|
}[log_level]
|
|
66
68
|
|
|
67
|
-
log_file_path =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
log_file_path = (
|
|
70
|
+
platformdirs.user_log_path(
|
|
71
|
+
"iolanta",
|
|
72
|
+
ensure_exists=True,
|
|
73
|
+
)
|
|
74
|
+
/ "iolanta.log"
|
|
75
|
+
)
|
|
71
76
|
|
|
72
|
-
# Get the level name first
|
|
73
77
|
level_name = {
|
|
74
|
-
logging.DEBUG:
|
|
75
|
-
logging.INFO:
|
|
76
|
-
logging.WARNING:
|
|
77
|
-
logging.ERROR:
|
|
78
|
+
logging.DEBUG: "DEBUG",
|
|
79
|
+
logging.INFO: "INFO",
|
|
80
|
+
logging.WARNING: "WARNING",
|
|
81
|
+
logging.ERROR: "ERROR",
|
|
78
82
|
}[level]
|
|
79
|
-
|
|
80
|
-
# Configure global loguru logger BEFORE creating Iolanta instance
|
|
83
|
+
|
|
81
84
|
loguru.logger.remove()
|
|
82
85
|
loguru.logger.add(
|
|
83
86
|
log_file_path,
|
|
84
87
|
level=level_name,
|
|
85
|
-
format=
|
|
88
|
+
format="{time} {level} {message}",
|
|
86
89
|
enqueue=True,
|
|
87
90
|
)
|
|
88
91
|
loguru.logger.add(
|
|
89
92
|
sys.stderr,
|
|
90
93
|
level=level_name,
|
|
91
|
-
format=
|
|
94
|
+
format="{time} | {level:<8} | {name}:{function}:{line} - {message}",
|
|
92
95
|
)
|
|
93
96
|
loguru.logger.level(level_name)
|
|
94
|
-
|
|
95
|
-
# Use the global logger
|
|
96
|
-
logger = loguru.logger
|
|
97
|
-
|
|
98
|
-
node_url = URL(url)
|
|
99
|
-
if node_url.scheme and node_url.scheme != 'file':
|
|
100
|
-
node = URIRef(url)
|
|
101
97
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
return loguru.logger
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def handle_error(
|
|
102
|
+
error: Exception,
|
|
103
|
+
log_level: LogLevel,
|
|
104
|
+
use_markdown: bool = True,
|
|
105
|
+
) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Handle an error by checking log level and printing appropriately.
|
|
108
|
+
|
|
109
|
+
If log level is DEBUG or INFO, re-raise the error.
|
|
110
|
+
Otherwise, print it and exit with code 1.
|
|
111
|
+
"""
|
|
112
|
+
level = {
|
|
113
|
+
LogLevel.DEBUG: logging.DEBUG,
|
|
114
|
+
LogLevel.INFO: logging.INFO,
|
|
115
|
+
LogLevel.WARNING: logging.WARNING,
|
|
116
|
+
LogLevel.ERROR: logging.ERROR,
|
|
117
|
+
}[log_level]
|
|
118
|
+
|
|
119
|
+
if level in {logging.DEBUG, logging.INFO}:
|
|
120
|
+
raise error
|
|
121
|
+
|
|
122
|
+
if use_markdown:
|
|
123
|
+
console.print(
|
|
124
|
+
Markdown(
|
|
125
|
+
str(error),
|
|
126
|
+
justify="left",
|
|
127
|
+
),
|
|
105
128
|
)
|
|
106
|
-
|
|
107
129
|
else:
|
|
108
|
-
|
|
109
|
-
|
|
130
|
+
console.print(str(error))
|
|
131
|
+
|
|
132
|
+
raise Exit(1)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def create_query_node(query_result: QueryResult) -> Literal:
|
|
136
|
+
"""
|
|
137
|
+
Create a Literal node from a query result.
|
|
138
|
+
|
|
139
|
+
Converts QueryResult (SelectResult, Graph, or bool) into a Literal
|
|
140
|
+
with the appropriate datatype for facet rendering.
|
|
141
|
+
"""
|
|
142
|
+
match query_result:
|
|
143
|
+
case SelectResult():
|
|
144
|
+
return Literal(
|
|
145
|
+
query_result,
|
|
146
|
+
datatype=DATATYPES["sparql-select-result"],
|
|
147
|
+
)
|
|
148
|
+
case Graph():
|
|
149
|
+
return Literal(
|
|
150
|
+
query_result,
|
|
151
|
+
datatype=DATATYPES["sparql-construct-result"],
|
|
152
|
+
)
|
|
153
|
+
case bool():
|
|
154
|
+
return Literal(
|
|
155
|
+
query_result,
|
|
156
|
+
datatype=DATATYPES["sparql-ask-result"],
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def render_and_return(
|
|
161
|
+
node: Literal | URIRef,
|
|
162
|
+
as_datatype: str,
|
|
163
|
+
language: str = DEFAULT_LANGUAGE,
|
|
164
|
+
log_level: LogLevel = LogLevel.ERROR,
|
|
165
|
+
):
|
|
166
|
+
"""
|
|
167
|
+
Render a node.
|
|
168
|
+
|
|
169
|
+
The node must be either a URIRef (for URLs) or a Literal (for query results).
|
|
170
|
+
"""
|
|
171
|
+
logger = setup_logging(log_level)
|
|
172
|
+
|
|
173
|
+
# Determine Iolanta instance based on node type
|
|
174
|
+
if isinstance(node, Literal):
|
|
175
|
+
# Literal nodes (e.g., from query results) are used directly
|
|
176
|
+
# Use current directory as project_root for query results
|
|
110
177
|
iolanta: Iolanta = Iolanta(
|
|
111
178
|
language=Literal(language),
|
|
112
179
|
logger=logger,
|
|
113
|
-
project_root=
|
|
180
|
+
project_root=Path.cwd(),
|
|
114
181
|
)
|
|
182
|
+
elif isinstance(node, URIRef):
|
|
183
|
+
# URIRef - determine project_root if it's a file:// URI
|
|
184
|
+
if str(node).startswith("file://"):
|
|
185
|
+
path = Path(str(node).replace("file://", ""))
|
|
186
|
+
iolanta: Iolanta = Iolanta(
|
|
187
|
+
language=Literal(language),
|
|
188
|
+
logger=logger,
|
|
189
|
+
project_root=path,
|
|
190
|
+
)
|
|
191
|
+
else:
|
|
192
|
+
iolanta: Iolanta = Iolanta(
|
|
193
|
+
language=Literal(language),
|
|
194
|
+
logger=logger,
|
|
195
|
+
)
|
|
196
|
+
else:
|
|
197
|
+
# This should never happen due to type checking, but kept for safety
|
|
198
|
+
raise TypeError(f"Expected Literal or URIRef, got {type(node)}")
|
|
115
199
|
|
|
116
200
|
return iolanta.render(
|
|
117
|
-
node=
|
|
201
|
+
node=node,
|
|
118
202
|
as_datatype=decode_datatype(as_datatype),
|
|
119
203
|
)
|
|
120
204
|
|
|
121
205
|
|
|
122
|
-
@app.command(name=
|
|
123
|
-
def render_command(
|
|
124
|
-
url: Annotated[str, Argument()],
|
|
206
|
+
@app.command(name="render")
|
|
207
|
+
def render_command( # noqa: WPS231, WPS238, WPS210, C901
|
|
208
|
+
url: Annotated[str | None, Argument()] = None,
|
|
209
|
+
query: Annotated[
|
|
210
|
+
str | None,
|
|
211
|
+
Option(
|
|
212
|
+
"--query",
|
|
213
|
+
help="SPARQL query to execute.",
|
|
214
|
+
),
|
|
215
|
+
] = None,
|
|
125
216
|
as_datatype: Annotated[
|
|
126
|
-
str,
|
|
127
|
-
|
|
217
|
+
str | None,
|
|
218
|
+
Option(
|
|
219
|
+
"--as",
|
|
128
220
|
),
|
|
129
|
-
] =
|
|
221
|
+
] = None,
|
|
130
222
|
language: Annotated[
|
|
131
|
-
str,
|
|
132
|
-
|
|
223
|
+
str,
|
|
224
|
+
Option(
|
|
225
|
+
help="Data language to prefer.",
|
|
133
226
|
),
|
|
134
227
|
] = DEFAULT_LANGUAGE,
|
|
135
228
|
log_level: LogLevel = LogLevel.ERROR,
|
|
136
229
|
):
|
|
137
230
|
"""Render a given URL."""
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
LogLevel.DEBUG: logging.DEBUG,
|
|
143
|
-
LogLevel.INFO: logging.INFO,
|
|
144
|
-
LogLevel.WARNING: logging.WARNING,
|
|
145
|
-
LogLevel.ERROR: logging.ERROR,
|
|
146
|
-
}[log_level]
|
|
147
|
-
|
|
148
|
-
if level in {logging.DEBUG, logging.INFO}:
|
|
149
|
-
raise
|
|
231
|
+
if query is not None:
|
|
232
|
+
# For queries, default to 'table' format
|
|
233
|
+
if as_datatype is None:
|
|
234
|
+
as_datatype = "table"
|
|
150
235
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
236
|
+
# Setup logging and create Iolanta instance (unlikely to raise)
|
|
237
|
+
logger = setup_logging(log_level)
|
|
238
|
+
iolanta: Iolanta = Iolanta(
|
|
239
|
+
language=Literal(language),
|
|
240
|
+
logger=logger,
|
|
241
|
+
project_root=Path.cwd(),
|
|
156
242
|
)
|
|
157
|
-
raise Exit(1)
|
|
158
243
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
244
|
+
try:
|
|
245
|
+
renderable = render_and_return(
|
|
246
|
+
node=create_query_node(iolanta.query(query)),
|
|
247
|
+
as_datatype=as_datatype,
|
|
248
|
+
language=language,
|
|
249
|
+
log_level=log_level,
|
|
250
|
+
)
|
|
251
|
+
except (SPARQLParseException, DocumentedError, FacetNotFound) as error:
|
|
252
|
+
handle_error(error, log_level, use_markdown=True)
|
|
253
|
+
except Exception as error:
|
|
254
|
+
handle_error(error, log_level, use_markdown=False)
|
|
255
|
+
else:
|
|
256
|
+
# FIXME: An intermediary Literal can be used to dispatch rendering.
|
|
257
|
+
match renderable:
|
|
258
|
+
case Table() as table:
|
|
259
|
+
console.print(table)
|
|
260
|
+
|
|
261
|
+
case unknown:
|
|
262
|
+
console.print(unknown)
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
if url is None:
|
|
266
|
+
console.print("Error: Either URL or --query must be provided")
|
|
171
267
|
raise Exit(1)
|
|
268
|
+
|
|
269
|
+
# For URLs, default to interactive mode
|
|
270
|
+
if as_datatype is None:
|
|
271
|
+
as_datatype = "https://iolanta.tech/cli/interactive"
|
|
272
|
+
|
|
273
|
+
# Parse string URL to URIRef (URL() is permissive and won't raise)
|
|
274
|
+
node_url = URL(url)
|
|
275
|
+
if node_url.scheme and node_url.scheme != "file":
|
|
276
|
+
node = URIRef(url)
|
|
277
|
+
else:
|
|
278
|
+
path = Path(node_url.path).absolute()
|
|
279
|
+
node = URIRef(f"file://{path}")
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
renderable = render_and_return(
|
|
283
|
+
node=node,
|
|
284
|
+
as_datatype=as_datatype,
|
|
285
|
+
language=language,
|
|
286
|
+
log_level=log_level,
|
|
287
|
+
)
|
|
288
|
+
except DocumentedError as error:
|
|
289
|
+
handle_error(error, log_level, use_markdown=True)
|
|
290
|
+
except Exception as error:
|
|
291
|
+
handle_error(error, log_level, use_markdown=False)
|
|
172
292
|
else:
|
|
173
293
|
# FIXME: An intermediary Literal can be used to dispatch rendering.
|
|
174
294
|
match renderable:
|
|
@@ -176,4 +296,4 @@ def render_command( # noqa: WPS231, WPS238, WPS210, C901
|
|
|
176
296
|
console.print(table)
|
|
177
297
|
|
|
178
298
|
case unknown:
|
|
179
|
-
print(unknown)
|
|
299
|
+
console.print(unknown)
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from rdflib import Literal
|
|
4
|
+
|
|
5
|
+
from iolanta.facets.cli.base import Renderable, RichFacet
|
|
6
|
+
from iolanta.facets.errors import NotALiteral
|
|
7
|
+
|
|
8
|
+
META = Path(__file__).parent / 'data' / 'query_result.yamlld'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AskResultCsvFacet(RichFacet):
|
|
12
|
+
"""Render ASK query results as CSV."""
|
|
13
|
+
|
|
14
|
+
META = META
|
|
15
|
+
|
|
16
|
+
def show(self) -> Renderable:
|
|
17
|
+
"""Render bool (ASK result) as CSV."""
|
|
18
|
+
if not isinstance(self.this, Literal):
|
|
19
|
+
raise NotALiteral(node=self.this)
|
|
20
|
+
|
|
21
|
+
query_result = self.this.value
|
|
22
|
+
|
|
23
|
+
return "result\n" + str(query_result).lower()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from rdflib import Literal
|
|
5
|
+
|
|
6
|
+
from iolanta.facets.cli.base import Renderable, RichFacet
|
|
7
|
+
from iolanta.facets.errors import NotALiteral
|
|
8
|
+
|
|
9
|
+
META = Path(__file__).parent / 'data' / 'query_result.yamlld'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AskResultJsonFacet(RichFacet):
|
|
13
|
+
"""Render ASK query results as JSON."""
|
|
14
|
+
|
|
15
|
+
META = META
|
|
16
|
+
|
|
17
|
+
def show(self) -> Renderable:
|
|
18
|
+
"""Render bool (ASK result) as JSON."""
|
|
19
|
+
if not isinstance(self.this, Literal):
|
|
20
|
+
raise NotALiteral(node=self.this)
|
|
21
|
+
|
|
22
|
+
query_result = self.this.value
|
|
23
|
+
|
|
24
|
+
return json.dumps(query_result)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from rdflib import Literal
|
|
4
|
+
|
|
5
|
+
from iolanta.facets.cli.base import Renderable, RichFacet
|
|
6
|
+
from iolanta.facets.errors import NotALiteral
|
|
7
|
+
|
|
8
|
+
META = Path(__file__).parent / 'data' / 'query_result.yamlld'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AskResultTableFacet(RichFacet):
|
|
12
|
+
"""Render ASK query results as table/text."""
|
|
13
|
+
|
|
14
|
+
META = META
|
|
15
|
+
|
|
16
|
+
def show(self) -> Renderable:
|
|
17
|
+
"""Render bool (ASK result) as text."""
|
|
18
|
+
if not isinstance(self.this, Literal):
|
|
19
|
+
raise NotALiteral(node=self.this)
|
|
20
|
+
|
|
21
|
+
query_result = self.this.value
|
|
22
|
+
|
|
23
|
+
return "✅ `True`" if query_result else "❌ `False`"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import io
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import cast
|
|
5
|
+
|
|
6
|
+
from rdflib import Graph, Literal
|
|
7
|
+
|
|
8
|
+
from iolanta.facets.cli.base import Renderable, RichFacet
|
|
9
|
+
from iolanta.facets.errors import NotALiteral
|
|
10
|
+
|
|
11
|
+
META = Path(__file__).parent / 'data' / 'query_result.yamlld'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConstructResultCsvFacet(RichFacet):
|
|
15
|
+
"""Render CONSTRUCT query results as CSV."""
|
|
16
|
+
|
|
17
|
+
META = META
|
|
18
|
+
|
|
19
|
+
def show(self) -> Renderable:
|
|
20
|
+
"""Render Graph (CONSTRUCT result) as CSV."""
|
|
21
|
+
if not isinstance(self.this, Literal):
|
|
22
|
+
raise NotALiteral(node=self.this)
|
|
23
|
+
|
|
24
|
+
graph = cast(Graph, self.this.value)
|
|
25
|
+
|
|
26
|
+
if not graph:
|
|
27
|
+
return ""
|
|
28
|
+
|
|
29
|
+
output = io.StringIO()
|
|
30
|
+
writer = csv.writer(output)
|
|
31
|
+
writer.writerow(('subject', 'predicate', 'object'))
|
|
32
|
+
writer.writerows(graph)
|
|
33
|
+
|
|
34
|
+
return output.getvalue()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import cast
|
|
4
|
+
|
|
5
|
+
from rdflib import Graph, Literal
|
|
6
|
+
|
|
7
|
+
from iolanta.facets.cli.base import Renderable, RichFacet
|
|
8
|
+
from iolanta.facets.errors import NotALiteral
|
|
9
|
+
|
|
10
|
+
META = Path(__file__).parent / 'data' / 'query_result.yamlld'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ConstructResultJsonFacet(RichFacet):
|
|
14
|
+
"""Render CONSTRUCT query results as JSON."""
|
|
15
|
+
|
|
16
|
+
META = META
|
|
17
|
+
|
|
18
|
+
def show(self) -> Renderable:
|
|
19
|
+
"""Render Graph (CONSTRUCT result) as JSON."""
|
|
20
|
+
if not isinstance(self.this, Literal):
|
|
21
|
+
raise NotALiteral(node=self.this)
|
|
22
|
+
|
|
23
|
+
graph = cast(Graph, self.this.value)
|
|
24
|
+
fieldnames = ('subject', 'predicate', 'object')
|
|
25
|
+
return json.dumps(
|
|
26
|
+
[
|
|
27
|
+
dict(zip(fieldnames, triple))
|
|
28
|
+
for triple in graph
|
|
29
|
+
],
|
|
30
|
+
indent=2,
|
|
31
|
+
default=str,
|
|
32
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import cast
|
|
3
|
+
|
|
4
|
+
from more_itertools import consume
|
|
5
|
+
from rdflib import Graph, Literal
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
|
|
8
|
+
from iolanta.cli.formatters.pretty import pretty_print_value
|
|
9
|
+
from iolanta.facets.cli.base import Renderable, RichFacet
|
|
10
|
+
from iolanta.facets.errors import NotALiteral
|
|
11
|
+
|
|
12
|
+
META = Path(__file__).parent / 'data' / 'query_result.yamlld'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ConstructResultTableFacet(RichFacet):
|
|
16
|
+
"""Render CONSTRUCT query results as table."""
|
|
17
|
+
|
|
18
|
+
META = META
|
|
19
|
+
|
|
20
|
+
def show(self) -> Renderable:
|
|
21
|
+
"""Render Graph (CONSTRUCT result) as table."""
|
|
22
|
+
if not isinstance(self.this, Literal):
|
|
23
|
+
raise NotALiteral(node=self.this)
|
|
24
|
+
|
|
25
|
+
graph = cast(Graph, self.this.value)
|
|
26
|
+
|
|
27
|
+
if not graph:
|
|
28
|
+
table = Table(
|
|
29
|
+
'Subject',
|
|
30
|
+
'Predicate',
|
|
31
|
+
'Object',
|
|
32
|
+
show_header=True,
|
|
33
|
+
header_style="bold magenta",
|
|
34
|
+
)
|
|
35
|
+
return table
|
|
36
|
+
|
|
37
|
+
table = Table(
|
|
38
|
+
'Subject',
|
|
39
|
+
'Predicate',
|
|
40
|
+
'Object',
|
|
41
|
+
show_header=True,
|
|
42
|
+
header_style="bold magenta",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
consume(
|
|
46
|
+
table.add_row(
|
|
47
|
+
*[
|
|
48
|
+
str(pretty_print_value(value))
|
|
49
|
+
for value in triple
|
|
50
|
+
],
|
|
51
|
+
)
|
|
52
|
+
for triple in graph
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return table
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
owl: http://www.w3.org/2002/07/owl#
|
|
7
|
+
|
|
8
|
+
$: rdfs:label
|
|
9
|
+
→:
|
|
10
|
+
"@type": "@id"
|
|
11
|
+
"@id": iolanta:outputs
|
|
12
|
+
⊆:
|
|
13
|
+
"@type": "@id"
|
|
14
|
+
"@id": rdfs:subClassOf
|
|
15
|
+
iolanta:hasDatatypeFacet:
|
|
16
|
+
"@type": "@id"
|
|
17
|
+
|
|
18
|
+
$included:
|
|
19
|
+
# Define the three SPARQL result datatypes
|
|
20
|
+
- $id: https://iolanta.tech/datatypes/sparql-select-result
|
|
21
|
+
$type: owl:Class
|
|
22
|
+
⊆: rdfs:Datatype
|
|
23
|
+
$: SPARQL SELECT Result
|
|
24
|
+
rdfs:comment: Datatype for SPARQL SELECT query results (variable bindings)
|
|
25
|
+
|
|
26
|
+
- $id: https://iolanta.tech/datatypes/sparql-construct-result
|
|
27
|
+
$type: owl:Class
|
|
28
|
+
⊆: rdfs:Datatype
|
|
29
|
+
$: SPARQL CONSTRUCT Result
|
|
30
|
+
rdfs:comment: Datatype for SPARQL CONSTRUCT query results (RDF graph)
|
|
31
|
+
|
|
32
|
+
- $id: https://iolanta.tech/datatypes/sparql-ask-result
|
|
33
|
+
$type: owl:Class
|
|
34
|
+
⊆: rdfs:Datatype
|
|
35
|
+
$: SPARQL ASK Result
|
|
36
|
+
rdfs:comment: Datatype for SPARQL ASK query results (boolean)
|
|
37
|
+
|
|
38
|
+
# Register SelectResult facets for each output format
|
|
39
|
+
- $id: pkg:pypi/iolanta#select-result-table-facet
|
|
40
|
+
$type: iolanta:Facet
|
|
41
|
+
$: SPARQL SELECT Result Table Facet
|
|
42
|
+
→: https://iolanta.tech/datatypes/table
|
|
43
|
+
|
|
44
|
+
- $id: pkg:pypi/iolanta#select-result-json-facet
|
|
45
|
+
$type: iolanta:Facet
|
|
46
|
+
$: SPARQL SELECT Result JSON Facet
|
|
47
|
+
→: https://iolanta.tech/datatypes/json
|
|
48
|
+
|
|
49
|
+
- $id: pkg:pypi/iolanta#select-result-csv-facet
|
|
50
|
+
$type: iolanta:Facet
|
|
51
|
+
$: SPARQL SELECT Result CSV Facet
|
|
52
|
+
→: https://iolanta.tech/datatypes/csv
|
|
53
|
+
|
|
54
|
+
- $id: https://iolanta.tech/datatypes/sparql-select-result
|
|
55
|
+
iolanta:hasDatatypeFacet:
|
|
56
|
+
- pkg:pypi/iolanta#select-result-table-facet
|
|
57
|
+
- pkg:pypi/iolanta#select-result-json-facet
|
|
58
|
+
- pkg:pypi/iolanta#select-result-csv-facet
|
|
59
|
+
|
|
60
|
+
# Register ConstructResult facets for each output format
|
|
61
|
+
- $id: pkg:pypi/iolanta#construct-result-table-facet
|
|
62
|
+
$type: iolanta:Facet
|
|
63
|
+
$: SPARQL CONSTRUCT Result Table Facet
|
|
64
|
+
→: https://iolanta.tech/datatypes/table
|
|
65
|
+
|
|
66
|
+
- $id: pkg:pypi/iolanta#construct-result-json-facet
|
|
67
|
+
$type: iolanta:Facet
|
|
68
|
+
$: SPARQL CONSTRUCT Result JSON Facet
|
|
69
|
+
→: https://iolanta.tech/datatypes/json
|
|
70
|
+
|
|
71
|
+
- $id: pkg:pypi/iolanta#construct-result-csv-facet
|
|
72
|
+
$type: iolanta:Facet
|
|
73
|
+
$: SPARQL CONSTRUCT Result CSV Facet
|
|
74
|
+
→: https://iolanta.tech/datatypes/csv
|
|
75
|
+
|
|
76
|
+
- $id: https://iolanta.tech/datatypes/sparql-construct-result
|
|
77
|
+
iolanta:hasDatatypeFacet:
|
|
78
|
+
- pkg:pypi/iolanta#construct-result-table-facet
|
|
79
|
+
- pkg:pypi/iolanta#construct-result-json-facet
|
|
80
|
+
- pkg:pypi/iolanta#construct-result-csv-facet
|
|
81
|
+
|
|
82
|
+
# Register AskResult facets for each output format
|
|
83
|
+
- $id: pkg:pypi/iolanta#ask-result-table-facet
|
|
84
|
+
$type: iolanta:Facet
|
|
85
|
+
$: SPARQL ASK Result Table Facet
|
|
86
|
+
→: https://iolanta.tech/datatypes/table
|
|
87
|
+
|
|
88
|
+
- $id: pkg:pypi/iolanta#ask-result-json-facet
|
|
89
|
+
$type: iolanta:Facet
|
|
90
|
+
$: SPARQL ASK Result JSON Facet
|
|
91
|
+
→: https://iolanta.tech/datatypes/json
|
|
92
|
+
|
|
93
|
+
- $id: pkg:pypi/iolanta#ask-result-csv-facet
|
|
94
|
+
$type: iolanta:Facet
|
|
95
|
+
$: SPARQL ASK Result CSV Facet
|
|
96
|
+
→: https://iolanta.tech/datatypes/csv
|
|
97
|
+
|
|
98
|
+
- $id: https://iolanta.tech/datatypes/sparql-ask-result
|
|
99
|
+
iolanta:hasDatatypeFacet:
|
|
100
|
+
- pkg:pypi/iolanta#ask-result-table-facet
|
|
101
|
+
- pkg:pypi/iolanta#ask-result-json-facet
|
|
102
|
+
- pkg:pypi/iolanta#ask-result-csv-facet
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import io
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from rdflib import Literal
|
|
6
|
+
|
|
7
|
+
from iolanta.facets.cli.base import Renderable, RichFacet
|
|
8
|
+
from iolanta.facets.errors import NotALiteral
|
|
9
|
+
|
|
10
|
+
META = Path(__file__).parent / 'data' / 'query_result.yamlld'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SelectResultCsvFacet(RichFacet):
|
|
14
|
+
"""Render SELECT query results as CSV."""
|
|
15
|
+
|
|
16
|
+
META = META
|
|
17
|
+
|
|
18
|
+
def show(self) -> Renderable:
|
|
19
|
+
"""Render SelectResult as CSV."""
|
|
20
|
+
if not isinstance(self.this, Literal):
|
|
21
|
+
raise NotALiteral(node=self.this)
|
|
22
|
+
|
|
23
|
+
query_result = self.this.value
|
|
24
|
+
|
|
25
|
+
if not query_result:
|
|
26
|
+
return ""
|
|
27
|
+
|
|
28
|
+
output = io.StringIO()
|
|
29
|
+
first_row = query_result[0]
|
|
30
|
+
fieldnames = first_row.keys()
|
|
31
|
+
|
|
32
|
+
writer = csv.DictWriter(output, fieldnames=fieldnames)
|
|
33
|
+
writer.writeheader()
|
|
34
|
+
writer.writerows(query_result)
|
|
35
|
+
|
|
36
|
+
return output.getvalue()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from rdflib import Literal
|
|
5
|
+
|
|
6
|
+
from iolanta.facets.cli.base import Renderable, RichFacet
|
|
7
|
+
from iolanta.facets.errors import NotALiteral
|
|
8
|
+
|
|
9
|
+
META = Path(__file__).parent / 'data' / 'query_result.yamlld'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SelectResultJsonFacet(RichFacet):
|
|
13
|
+
"""Render SELECT query results as JSON."""
|
|
14
|
+
|
|
15
|
+
META = META
|
|
16
|
+
|
|
17
|
+
def show(self) -> Renderable:
|
|
18
|
+
"""Render SelectResult as JSON."""
|
|
19
|
+
if not isinstance(self.this, Literal):
|
|
20
|
+
raise NotALiteral(node=self.this)
|
|
21
|
+
|
|
22
|
+
query_result = self.this.value
|
|
23
|
+
|
|
24
|
+
return json.dumps(query_result, indent=2, default=str)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from more_itertools import consume, first
|
|
4
|
+
from rdflib import Literal
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
|
|
7
|
+
from iolanta.cli.formatters.pretty import pretty_print_value
|
|
8
|
+
from iolanta.facets.cli.base import Renderable, RichFacet
|
|
9
|
+
from iolanta.facets.errors import NotALiteral
|
|
10
|
+
|
|
11
|
+
META = Path(__file__).parent / 'data' / 'query_result.yamlld'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SelectResultTableFacet(RichFacet):
|
|
15
|
+
"""Render SELECT query results as table."""
|
|
16
|
+
|
|
17
|
+
META = META
|
|
18
|
+
|
|
19
|
+
def show(self) -> Renderable:
|
|
20
|
+
"""Render SelectResult as table."""
|
|
21
|
+
if not isinstance(self.this, Literal):
|
|
22
|
+
raise NotALiteral(node=self.this)
|
|
23
|
+
|
|
24
|
+
query_result = self.this.value
|
|
25
|
+
|
|
26
|
+
if not query_result:
|
|
27
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
28
|
+
return table
|
|
29
|
+
|
|
30
|
+
columns = first(query_result).keys()
|
|
31
|
+
|
|
32
|
+
table = Table(
|
|
33
|
+
*columns,
|
|
34
|
+
show_header=True,
|
|
35
|
+
header_style="bold magenta",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
consume(
|
|
39
|
+
table.add_row(
|
|
40
|
+
*[
|
|
41
|
+
str(pretty_print_value(value))
|
|
42
|
+
for value in row.values()
|
|
43
|
+
],
|
|
44
|
+
)
|
|
45
|
+
for row in query_result
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return table
|
iolanta/mcp/cli.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
from pathlib import Path
|
|
1
2
|
from typing import Annotated
|
|
2
3
|
|
|
3
4
|
from fastmcp import FastMCP
|
|
5
|
+
from rdflib import URIRef
|
|
6
|
+
from yarl import URL
|
|
4
7
|
|
|
5
8
|
from iolanta.cli.main import render_and_return
|
|
6
9
|
|
|
@@ -9,11 +12,21 @@ mcp = FastMCP("Iolanta MCP Server")
|
|
|
9
12
|
|
|
10
13
|
@mcp.tool()
|
|
11
14
|
def render_uri(
|
|
12
|
-
uri: Annotated[str,
|
|
13
|
-
as_format: Annotated[
|
|
15
|
+
uri: Annotated[str, "URL, or file system path, to render"],
|
|
16
|
+
as_format: Annotated[
|
|
17
|
+
str, "Format to render as. Examples: `labeled-triple-set`, `mermaid`"
|
|
18
|
+
],
|
|
14
19
|
) -> str:
|
|
15
20
|
"""Render a URI."""
|
|
16
|
-
|
|
21
|
+
# Parse string URL to URIRef
|
|
22
|
+
node_url = URL(uri)
|
|
23
|
+
if node_url.scheme and node_url.scheme != "file":
|
|
24
|
+
node = URIRef(uri)
|
|
25
|
+
else:
|
|
26
|
+
path = Path(node_url.path).absolute()
|
|
27
|
+
node = URIRef(f"file://{path}")
|
|
28
|
+
|
|
29
|
+
return str(render_and_return(node=node, as_datatype=as_format))
|
|
17
30
|
|
|
18
31
|
|
|
19
32
|
def app():
|
iolanta/mermaid/models.py
CHANGED
|
@@ -12,7 +12,10 @@ from iolanta.models import NotLiteralNode # noqa: WPS202
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def escape_label(label: str) -> str:
|
|
15
|
-
"""Escape a label
|
|
15
|
+
"""Escape a label and return it wrapped in appropriate quotes.
|
|
16
|
+
|
|
17
|
+
Returns the label with URLs stripped, quotes escaped, and wrapped in quotes.
|
|
18
|
+
Uses single quotes if label contains double quotes to avoid escaping issues.
|
|
16
19
|
|
|
17
20
|
Escapes quotes in labels that will be wrapped in quotes in Mermaid syntax.
|
|
18
21
|
"""
|
|
@@ -20,8 +23,15 @@ def escape_label(label: str) -> str:
|
|
|
20
23
|
safe_label = (
|
|
21
24
|
label.replace("https://", "").replace("http://", "").replace("www.", "")
|
|
22
25
|
)
|
|
23
|
-
#
|
|
24
|
-
|
|
26
|
+
# Use single quotes if label contains double quotes to avoid escaping issues
|
|
27
|
+
use_single = '"' in safe_label
|
|
28
|
+
quote_char = "'" if use_single else '"'
|
|
29
|
+
# Escape the quote character that will be used for wrapping
|
|
30
|
+
if use_single:
|
|
31
|
+
escaped_label = safe_label.replace("'", r"\'")
|
|
32
|
+
else:
|
|
33
|
+
escaped_label = safe_label.replace('"', r"\"")
|
|
34
|
+
return f"{quote_char}{escaped_label}{quote_char}"
|
|
25
35
|
|
|
26
36
|
|
|
27
37
|
class Direction(enum.StrEnum):
|
|
@@ -54,14 +64,13 @@ class MermaidURINode(MermaidScalar, frozen=True):
|
|
|
54
64
|
def maybe_title(self):
|
|
55
65
|
if not self.title:
|
|
56
66
|
return ""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return f'("{safe_title}")'
|
|
67
|
+
quoted_title = escape_label(self.title)
|
|
68
|
+
return f"({quoted_title})"
|
|
60
69
|
|
|
61
70
|
@property
|
|
62
71
|
def id(self):
|
|
63
72
|
return re.sub(
|
|
64
|
-
r"[:\/\.#()]", "_", urllib_parse.unquote(str(self.url)).strip("/")
|
|
73
|
+
r"[:\/\.#()?=&+]", "_", urllib_parse.unquote(str(self.url)).strip("/")
|
|
65
74
|
)
|
|
66
75
|
|
|
67
76
|
|
|
@@ -85,7 +94,7 @@ class MermaidLiteral(MermaidScalar, frozen=True):
|
|
|
85
94
|
|
|
86
95
|
|
|
87
96
|
class MermaidBlankNode(MermaidScalar):
|
|
88
|
-
"""{self.id}(
|
|
97
|
+
"""{self.id}({self.escaped_title})"""
|
|
89
98
|
|
|
90
99
|
node: BNode
|
|
91
100
|
title: str
|
|
@@ -102,7 +111,7 @@ class MermaidBlankNode(MermaidScalar):
|
|
|
102
111
|
|
|
103
112
|
class MermaidEdge(MermaidScalar):
|
|
104
113
|
"""
|
|
105
|
-
{self.source.id} --- {self.id}([
|
|
114
|
+
{self.source.id} --- {self.id}([{self.escaped_title}])--> {self.target.id}
|
|
106
115
|
click {self.id} "{self.predicate}"
|
|
107
116
|
class {self.id} predicate
|
|
108
117
|
"""
|
|
@@ -130,7 +139,7 @@ class MermaidEdge(MermaidScalar):
|
|
|
130
139
|
|
|
131
140
|
class MermaidSubgraph(Documented, BaseModel, arbitrary_types_allowed=True, frozen=True):
|
|
132
141
|
"""
|
|
133
|
-
subgraph {self.id}[
|
|
142
|
+
subgraph {self.id}[{self.escaped_title}]
|
|
134
143
|
direction {self.direction}
|
|
135
144
|
{self.formatted_body}
|
|
136
145
|
end
|
|
@@ -6,7 +6,7 @@ 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=T1l1ilWp818pF3oR6VJIlPQ_ElicN7ECGlcJm5XiUqo,8170
|
|
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
|
|
@@ -61,6 +61,17 @@ iolanta/facets/mkdocs_material_insiders_markdown/facet.py,sha256=EY-H4gR5YgiDVt2
|
|
|
61
61
|
iolanta/facets/mkdocs_material_insiders_markdown/templates/datatype.jinja2.md,sha256=k9GSdy27mAY3eRL899pk6ZCYr4ZpEY1EuM5RY-OApYM,551
|
|
62
62
|
iolanta/facets/page_title.py,sha256=TwgZK2g_e5UoWYjKNgDzzkmq1EI3cY58680iC8N9kZI,1407
|
|
63
63
|
iolanta/facets/qname.py,sha256=Z2wjDWV90Z4vuwLj31MSf5EBGTb0dxzjlKl-Iv4dPao,533
|
|
64
|
+
iolanta/facets/query/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
|
+
iolanta/facets/query/ask_result_csv.py,sha256=oDuk_a8RiTsyXZuPraUSKswC7dLTCjSFJU8tyRaLALY,589
|
|
66
|
+
iolanta/facets/query/ask_result_json.py,sha256=qXKIg1AdVPixd3SDY_nyXleJ7y4Q94kPFnanzsCmcTM,590
|
|
67
|
+
iolanta/facets/query/ask_result_table.py,sha256=QdmrhKofca7ZOca9SN7GA3Riite1NPOsrKdLvW0xXkU,608
|
|
68
|
+
iolanta/facets/query/construct_result_csv.py,sha256=70KYP9aSnGSm_Gxk6Wjcik4YkvsdmAlO5EwNVOE59Nc,850
|
|
69
|
+
iolanta/facets/query/construct_result_json.py,sha256=mdaxGReivhHGtK_0OCMky6BcQ7EGth0-nEUMNy4XnpE,856
|
|
70
|
+
iolanta/facets/query/construct_result_table.py,sha256=bCUHj0eW086Rx7g6gW432j8Js4vFNCNceH_AJhnJ7po,1398
|
|
71
|
+
iolanta/facets/query/data/query_result.yamlld,sha256=uutBc4XEYh3LSVeYdvrs5uVsdDU7e9TQ2TQEtDfzaks,3440
|
|
72
|
+
iolanta/facets/query/select_result_csv.py,sha256=Js5h_MJ-DgWGJbhypuLoJ0UiW7wsS8rG23aMCAOGdd0,880
|
|
73
|
+
iolanta/facets/query/select_result_json.py,sha256=gajYXgROSWphYeimfO0W1xumsMLdpy_d57QgYQEI3a4,614
|
|
74
|
+
iolanta/facets/query/select_result_table.py,sha256=ePeUydu4bV7MZciZriPreIKgshrtfd8qn9qQC59hsBg,1236
|
|
64
75
|
iolanta/facets/textual_browser/__init__.py,sha256=sKgDvXOwib9n9d63kdtKCEv26-FoL0VN6zxDmfcheZ8,104
|
|
65
76
|
iolanta/facets/textual_browser/app.py,sha256=rF34KbWi0L-Mozwzm-wzBS-3OqCcwbaXl0GZCl4YpAg,3503
|
|
66
77
|
iolanta/facets/textual_browser/facet.py,sha256=5mX1l6P-Ga7buzXmItxSpta6G_D4Fvwv8H6mU8-3g80,742
|
|
@@ -124,11 +135,11 @@ iolanta/labeled_triple_set/data/labeled_triple_set.yamlld,sha256=P3oAPSPsirpbcRX
|
|
|
124
135
|
iolanta/labeled_triple_set/labeled_triple_set.py,sha256=o4IgvTvPd0mzBtpgHYd4n1xpujYdAvWBr6gIYwp5vnA,4061
|
|
125
136
|
iolanta/labeled_triple_set/sparql/triples.sparql,sha256=VsCmYN5AX7jSIiFm-SqLcRcOvUVj8yyZI4PSzKROtQw,82
|
|
126
137
|
iolanta/mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
127
|
-
iolanta/mcp/cli.py,sha256=
|
|
138
|
+
iolanta/mcp/cli.py,sha256=QRHrkGnfxXNZx_iUwTbXz4zZwvp5N_ceKoB8ae_fLzY,823
|
|
128
139
|
iolanta/mermaid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
129
140
|
iolanta/mermaid/facet.py,sha256=8mLOBrzlY84jiWhtJNY5BkXPDpRhL2OB1LUwwwNS1X0,4065
|
|
130
141
|
iolanta/mermaid/mermaid.yamlld,sha256=G_8lqNfs6S7pz9koDC8xAaic4EaHsYnFLgexDVyMuCU,552
|
|
131
|
-
iolanta/mermaid/models.py,sha256=
|
|
142
|
+
iolanta/mermaid/models.py,sha256=OzY54DZbxmOCqlfyfin2h8NR3EytdgwlNhbpnY_PH7Y,5330
|
|
132
143
|
iolanta/mermaid/sparql/ask-has-triples.sparql,sha256=mOYJ_rutEG_15PKTCHSv2GqzbkAawIn1U2kjkIr_Me0,41
|
|
133
144
|
iolanta/mermaid/sparql/graph.sparql,sha256=mDGf05od3CUFhzI6rcqt5ZMVy-gSKDu-WxmV_zpIsVI,62
|
|
134
145
|
iolanta/mermaid/sparql/subgraphs.sparql,sha256=VuoOYr_ZtKXXRrBpAEJek0mBRzR9EV-KnKENgAbkzCs,71
|
|
@@ -154,7 +165,7 @@ iolanta/sparqlspace/sparqlspace.py,sha256=Y8_ZPXwuGEXbEes6XQjaQWA2Zv9y8SWxMPDFdq
|
|
|
154
165
|
iolanta/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
155
166
|
iolanta/widgets/description.py,sha256=98Qd3FwT9r8sYqKjl9ZEptaVX9jJ2ULWf0uy3j52p5o,800
|
|
156
167
|
iolanta/widgets/mixin.py,sha256=nDRCOc-gizCf1a5DAcYs4hW8eZEd6pHBPFsfm0ncv7E,251
|
|
157
|
-
iolanta-2.1.
|
|
158
|
-
iolanta-2.1.
|
|
159
|
-
iolanta-2.1.
|
|
160
|
-
iolanta-2.1.
|
|
168
|
+
iolanta-2.1.13.dist-info/METADATA,sha256=9qsxicf-PrUK46WZ45pVAIpxd9d66ZgSNXkcncdfeFU,2317
|
|
169
|
+
iolanta-2.1.13.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
170
|
+
iolanta-2.1.13.dist-info/entry_points.txt,sha256=Z1f3OaNruE2a6eprkiLgcPw-lZCah_cRT-PTlDD-Y1s,2569
|
|
171
|
+
iolanta-2.1.13.dist-info/RECORD,,
|
|
@@ -4,7 +4,13 @@ iolanta-mcp=iolanta.mcp.cli:app
|
|
|
4
4
|
sparqlspace=iolanta.sparqlspace.cli:app
|
|
5
5
|
|
|
6
6
|
[iolanta.facets]
|
|
7
|
+
ask-result-csv-facet=iolanta.facets.query.ask_result_csv:AskResultCsvFacet
|
|
8
|
+
ask-result-json-facet=iolanta.facets.query.ask_result_json:AskResultJsonFacet
|
|
9
|
+
ask-result-table-facet=iolanta.facets.query.ask_result_table:AskResultTableFacet
|
|
7
10
|
boolean=iolanta.facets.generic:BoolLiteral
|
|
11
|
+
construct-result-csv-facet=iolanta.facets.query.construct_result_csv:ConstructResultCsvFacet
|
|
12
|
+
construct-result-json-facet=iolanta.facets.query.construct_result_json:ConstructResultJsonFacet
|
|
13
|
+
construct-result-table-facet=iolanta.facets.query.construct_result_table:ConstructResultTableFacet
|
|
8
14
|
icon=iolanta.facets.icon:IconFacet
|
|
9
15
|
labeled-triple-set=iolanta.labeled_triple_set.labeled_triple_set:LabeledTripleSet
|
|
10
16
|
mermaid-graph=iolanta.mermaid.facet:Mermaid
|
|
@@ -12,6 +18,9 @@ mermaid-roadmap=iolanta.facets.mermaid_roadmap.facet:MermaidRoadmap
|
|
|
12
18
|
mkdocs-material-insiders-markdown-datatype=iolanta.facets.mkdocs_material_insiders_markdown:MkDocsMaterialInsidersMarkdownFacet
|
|
13
19
|
qname=iolanta.facets.qname:QNameFacet
|
|
14
20
|
rich-declension-table=iolanta.declension.facet:RichDeclensionTable
|
|
21
|
+
select-result-csv-facet=iolanta.facets.query.select_result_csv:SelectResultCsvFacet
|
|
22
|
+
select-result-json-facet=iolanta.facets.query.select_result_json:SelectResultJsonFacet
|
|
23
|
+
select-result-table-facet=iolanta.facets.query.select_result_table:SelectResultTableFacet
|
|
15
24
|
textual-browser=iolanta.facets.textual_browser:TextualBrowserFacet
|
|
16
25
|
textual-class=iolanta.facets.textual_class:Class
|
|
17
26
|
textual-graph=iolanta.facets.textual_graph:GraphFacet
|