iolanta 2.1.11__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/facet.py +15 -9
- iolanta/facets/mermaid_roadmap/__init__.py +0 -0
- iolanta/facets/mermaid_roadmap/facet.py +133 -0
- iolanta/facets/mermaid_roadmap/inference/blocks.sparql +13 -0
- iolanta/facets/mermaid_roadmap/inference/has-task-default-type.sparql +16 -0
- iolanta/facets/mermaid_roadmap/inference/task.sparql +26 -0
- iolanta/facets/mermaid_roadmap/inference/unblocked.sparql +21 -0
- iolanta/facets/mermaid_roadmap/mermaid_roadmap.yamlld +59 -0
- iolanta/facets/mermaid_roadmap/sparql/edges.sparql +25 -0
- iolanta/facets/mermaid_roadmap/sparql/nodes.sparql +17 -0
- 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/iolanta.py +146 -55
- iolanta/mcp/cli.py +16 -3
- iolanta/mermaid/models.py +74 -40
- iolanta/sparqlspace/processor.py +232 -179
- {iolanta-2.1.11.dist-info → iolanta-2.1.13.dist-info}/METADATA +2 -2
- {iolanta-2.1.11.dist-info → iolanta-2.1.13.dist-info}/RECORD +30 -10
- {iolanta-2.1.11.dist-info → iolanta-2.1.13.dist-info}/WHEEL +1 -1
- {iolanta-2.1.11.dist-info → iolanta-2.1.13.dist-info}/entry_points.txt +10 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
PREFIX roadmap: <https://iolanta.tech/roadmap/>
|
|
2
|
+
|
|
3
|
+
SELECT DISTINCT ?source ?target WHERE {
|
|
4
|
+
# Find roadmap:blocks relationships across all graphs (including inferred)
|
|
5
|
+
?source roadmap:blocks ?target .
|
|
6
|
+
|
|
7
|
+
# Only include edges where both source and target are Task/Event/Bug nodes
|
|
8
|
+
# (they will be filtered by the nodes query, but this ensures we only show
|
|
9
|
+
# relevant edges)
|
|
10
|
+
{
|
|
11
|
+
?source a roadmap:Task .
|
|
12
|
+
} UNION {
|
|
13
|
+
?source a roadmap:Event .
|
|
14
|
+
} UNION {
|
|
15
|
+
?source a roadmap:Bug .
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
?target a roadmap:Task .
|
|
20
|
+
} UNION {
|
|
21
|
+
?target a roadmap:Event .
|
|
22
|
+
} UNION {
|
|
23
|
+
?target a roadmap:Bug .
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
PREFIX roadmap: <https://iolanta.tech/roadmap/>
|
|
2
|
+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
3
|
+
|
|
4
|
+
SELECT DISTINCT ?node ?is_unblocked WHERE {
|
|
5
|
+
# Search across all graphs (including inference graphs)
|
|
6
|
+
# This finds both original types and inferred types
|
|
7
|
+
{
|
|
8
|
+
?node rdf:type roadmap:Task .
|
|
9
|
+
} UNION {
|
|
10
|
+
?node rdf:type roadmap:Event .
|
|
11
|
+
} UNION {
|
|
12
|
+
?node rdf:type roadmap:Bug .
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
# Check if node is unblocked (has roadmap:Unblocked type)
|
|
16
|
+
BIND(EXISTS { ?node rdf:type roadmap:Unblocked } AS ?is_unblocked)
|
|
17
|
+
}
|
|
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
|