iolanta 2.0.1__tar.gz → 2.0.5__tar.gz

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 (136) hide show
  1. {iolanta-2.0.1 → iolanta-2.0.5}/PKG-INFO +1 -1
  2. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/data/context.yaml +15 -0
  3. iolanta-2.0.5/iolanta/data/graph-triples.yamlld +6 -0
  4. iolanta-2.0.5/iolanta/data/textual-browser.yaml +127 -0
  5. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/facet.py +5 -0
  6. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/locator.py +32 -1
  7. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_class/facets.py +35 -46
  8. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_class/sparql/instances.sparql +1 -1
  9. iolanta-2.0.5/iolanta/facets/textual_property_pairs_table.py +133 -0
  10. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/iolanta.py +3 -3
  11. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/parse_quads.py +0 -7
  12. iolanta-2.0.5/iolanta/sparqlspace/cli.py +64 -0
  13. {iolanta-2.0.1/iolanta/cyberspace → iolanta-2.0.5/iolanta/sparqlspace}/processor.py +113 -72
  14. iolanta-2.0.5/iolanta/sparqlspace/sparqlspace.py +32 -0
  15. {iolanta-2.0.1 → iolanta-2.0.5}/pyproject.toml +3 -3
  16. iolanta-2.0.1/iolanta/data/textual-browser.yaml +0 -109
  17. {iolanta-2.0.1 → iolanta-2.0.5}/README.md +0 -0
  18. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/__init__.py +0 -0
  19. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/base_plugin.py +0 -0
  20. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/cli/__init__.py +0 -0
  21. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/cli/formatters/__init__.py +0 -0
  22. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/cli/formatters/choose.py +0 -0
  23. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/cli/formatters/csv.py +0 -0
  24. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/cli/formatters/json.py +0 -0
  25. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/cli/formatters/pretty.py +0 -0
  26. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/cli/main.py +0 -0
  27. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/cli/models.py +0 -0
  28. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/cli/pretty_print.py +0 -0
  29. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/context.py +0 -0
  30. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/conversions.py +0 -0
  31. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/data/cli.yaml +0 -0
  32. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/data/html.yaml +0 -0
  33. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/data/iolanta.yaml +0 -0
  34. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/ensure_is_context.py +0 -0
  35. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/entry_points.py +0 -0
  36. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/errors.py +0 -0
  37. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/__init__.py +0 -0
  38. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/cli/__init__.py +0 -0
  39. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/cli/base.py +0 -0
  40. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/cli/default.py +0 -0
  41. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/cli/record.py +0 -0
  42. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/cli/sparql/link.sparql +0 -0
  43. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/cli/sparql/record.sparql +0 -0
  44. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/errors.py +0 -0
  45. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/foaf_person_title/__init__.py +0 -0
  46. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/foaf_person_title/facet.py +0 -0
  47. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/foaf_person_title/sparql/names.sparql +0 -0
  48. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/generic/__init__.py +0 -0
  49. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/generic/bool_literal.py +0 -0
  50. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/generic/date_literal.py +0 -0
  51. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/generic/default.py +0 -0
  52. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/generic/sparql/default.sparql +0 -0
  53. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/html/__init__.py +0 -0
  54. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/html/base.py +0 -0
  55. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/html/code_literal.py +0 -0
  56. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/html/default.py +0 -0
  57. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/icon.py +0 -0
  58. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/page_title.py +0 -0
  59. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/qname.py +0 -0
  60. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_browser/__init__.py +0 -0
  61. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_browser/app.py +0 -0
  62. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_browser/facet.py +0 -0
  63. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_browser/history.py +0 -0
  64. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_browser/home.py +0 -0
  65. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_browser/location.py +0 -0
  66. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_browser/models.py +0 -0
  67. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_browser/page.py +0 -0
  68. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_browser/page_switcher.py +0 -0
  69. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_class/__init__.py +0 -0
  70. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/__init__.py +0 -0
  71. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/facets.py +0 -0
  72. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/sparql/inverse-properties.sparql +0 -0
  73. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/sparql/label.sparql +0 -0
  74. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/sparql/nodes-for-property.sparql +0 -0
  75. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/sparql/properties.sparql +0 -0
  76. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/tcss/default.tcss +0 -0
  77. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/templates/default.md +0 -0
  78. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/triple_uri_ref.py +0 -0
  79. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_default/widgets.py +0 -0
  80. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_graph/__init__.py +0 -0
  81. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_graph/facets.py +0 -0
  82. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_graph/sparql/triples.sparql +0 -0
  83. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_graph_triples.py +0 -0
  84. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_link/__init__.py +0 -0
  85. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_link/facet.py +0 -0
  86. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_nanopublication/__init__.py +0 -0
  87. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_nanopublication/facet.py +0 -0
  88. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_nanopublication/models.py +0 -0
  89. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_nanopublication/nanopublication_widget.py +0 -0
  90. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_nanopublication/term_list_widget.py +0 -0
  91. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_nanopublication/term_widget.py +0 -0
  92. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_ontology/__init__.py +0 -0
  93. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_ontology/facets.py +0 -0
  94. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_ontology/sparql/terms.sparql +0 -0
  95. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_ontology/sparql/visualization-vocab.sparql +0 -0
  96. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_provenance/__init__.py +0 -0
  97. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_provenance/facets.py +0 -0
  98. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_provenance/sparql/graphs.sparql +0 -0
  99. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/textual_provenance/sparql/triples.sparql +0 -0
  100. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/title/__init__.py +0 -0
  101. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/title/facets.py +0 -0
  102. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/title/sparql/title.sparql +0 -0
  103. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/wikibase_statement_title/__init__.py +0 -0
  104. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/wikibase_statement_title/facets.py +0 -0
  105. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/facets/wikibase_statement_title/sparql/statement-title.sparql +0 -0
  106. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/loaders/__init__.py +0 -0
  107. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/loaders/base.py +0 -0
  108. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/loaders/data_type_choice.py +0 -0
  109. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/loaders/dict_loader.py +0 -0
  110. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/loaders/errors.py +0 -0
  111. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/loaders/http.py +0 -0
  112. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/loaders/local_directory.py +0 -0
  113. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/loaders/local_file.py +0 -0
  114. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/loaders/scheme_choice.py +0 -0
  115. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/models.py +0 -0
  116. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/namespaces.py +0 -0
  117. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/node_to_qname.py +0 -0
  118. {iolanta-2.0.1/iolanta/cyberspace → iolanta-2.0.5/iolanta/parsers}/__init__.py +0 -0
  119. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/parsers/base.py +0 -0
  120. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/parsers/dict_parser.py +0 -0
  121. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/parsers/errors.py +0 -0
  122. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/parsers/json.py +0 -0
  123. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/parsers/markdown.py +0 -0
  124. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/parsers/yaml.py +0 -0
  125. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/plugin.py +0 -0
  126. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/query_result.py +0 -0
  127. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/reformat_blank_nodes.py +0 -0
  128. {iolanta-2.0.1/iolanta/parsers → iolanta-2.0.5/iolanta/resolvers}/__init__.py +0 -0
  129. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/resolvers/base.py +0 -0
  130. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/resolvers/python_import.py +0 -0
  131. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/shortcuts.py +0 -0
  132. {iolanta-2.0.1/iolanta/resolvers → iolanta-2.0.5/iolanta/sparqlspace}/__init__.py +0 -0
  133. {iolanta-2.0.1/iolanta/cyberspace → iolanta-2.0.5/iolanta/sparqlspace}/inference/wikibase-claim.sparql +0 -0
  134. {iolanta-2.0.1/iolanta/cyberspace → iolanta-2.0.5/iolanta/sparqlspace}/inference/wikibase-statement-property.sparql +0 -0
  135. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/widgets/__init__.py +0 -0
  136. {iolanta-2.0.1 → iolanta-2.0.5}/iolanta/widgets/mixin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iolanta
3
- Version: 2.0.1
3
+ Version: 2.0.5
4
4
  Summary: Semantic Web browser
5
5
  License: MIT
6
6
  Author: Anatoly Scherbakov
@@ -59,3 +59,18 @@
59
59
 
60
60
  rdf:type:
61
61
  "@type": "@id"
62
+
63
+ $: rdfs:label
64
+ →:
65
+ "@type": "@id"
66
+ "@id": iolanta:outputs
67
+
68
+ ⊆:
69
+ "@type": "@id"
70
+ "@id": rdfs:subClassOf
71
+
72
+ ⪯:
73
+ "@type": "@id"
74
+ "@id": iolanta:is-preferred-over
75
+
76
+ ↦: iolanta:matches
@@ -0,0 +1,6 @@
1
+ "@context": context.yaml
2
+ $id: python://iolanta.facets.textual_graph.GraphFacet
3
+ $: Graph Triples
4
+ →: https://iolanta.tech/cli/textual
5
+ ⪯: python://iolanta.facets.textual_default.InverseProperties
6
+ ↦: ASK WHERE { GRAPH $this { ?s ?p ?o } }
@@ -0,0 +1,127 @@
1
+ "@context":
2
+ "@import": https://json-ld.org/contexts/dollar-convenience.jsonld
3
+ vann: https://purl.org/vocab/vann/
4
+ foaf: https://xmlns.com/foaf/0.1/
5
+ owl: https://www.w3.org/2002/07/owl#
6
+ iolanta: https://iolanta.tech/
7
+ rdfs: "https://www.w3.org/2000/01/rdf-schema#"
8
+ rdf: https://www.w3.org/1999/02/22-rdf-syntax-ns#
9
+ np: https://www.nanopub.org/nschema#
10
+ dcterms: https://purl.org/dc/terms/
11
+ xsd: https://www.w3.org/2001/XMLSchema#
12
+
13
+ iolanta:outputs:
14
+ "@type": "@id"
15
+
16
+ $: rdfs:label
17
+ →:
18
+ "@type": "@id"
19
+ "@id": iolanta:outputs
20
+
21
+ ⊆:
22
+ "@type": "@id"
23
+ "@id": rdfs:subClassOf
24
+
25
+ ⪯:
26
+ "@type": "@id"
27
+ "@id": iolanta:is-preferred-over
28
+
29
+ ↦: iolanta:matches
30
+
31
+ "@included":
32
+ - $id: iolanta:icon
33
+ $: Icon
34
+ ⊆: xsd:string
35
+
36
+ - $id: dcterms:creator
37
+ iolanta:icon: ©️
38
+
39
+ - $id: https://iolanta.tech/datatypes/icon
40
+ $: Icon
41
+ iolanta:hasDefaultFacet:
42
+ $id: python://iolanta.facets.icon.IconFacet
43
+ →: https://iolanta.tech/datatypes/icon
44
+
45
+ - $id: np:Nanopublication
46
+ $: Nanopublication
47
+ iolanta:hasInstanceFacet:
48
+ $id: python://iolanta.facets.textual_nanopublication.NanopublicationFacet
49
+ $: Nanopublication
50
+ →: https://iolanta.tech/cli/textual
51
+ ⪯:
52
+ - python://iolanta.facets.textual_default.TextualDefaultFacet
53
+ - python://iolanta.facets.textual_graph.GraphFacet
54
+
55
+ - $id: https://iolanta.tech/cli/interactive
56
+ iolanta:hasDefaultFacet:
57
+ $id: python://iolanta.facets.textual_browser.TextualBrowserFacet
58
+
59
+ - $id: python://iolanta.facets.textual_default.TextualDefaultFacet
60
+ $: Properties
61
+ ⪯: python://iolanta.facets.textual_default.InverseProperties
62
+ →: https://iolanta.tech/cli/textual
63
+ ↦: |
64
+ ASK WHERE {
65
+ GRAPH ?graph { $this ?property ?value }
66
+ FILTER (?graph != <iolanta://_meta>)
67
+ }
68
+
69
+ - $id: python://iolanta.facets.textual_default.InverseProperties
70
+ $: Inverse Properties
71
+ →: https://iolanta.tech/cli/textual
72
+ ↦: |
73
+ ASK WHERE {
74
+ GRAPH ?graph { ?something ?property $this }
75
+ FILTER (?graph != <iolanta://_meta>)
76
+ }
77
+
78
+ - $id: "urn:"
79
+ iolanta:hasFacetByPrefix:
80
+ $id: python://iolanta.facets.textual_provenance.TextualProvenanceFacet
81
+ →: https://iolanta.tech/cli/textual
82
+
83
+ - $id: https://iolanta.tech/cli/link
84
+ iolanta:hasDefaultFacet:
85
+ $id: python://iolanta.facets.textual_link.TextualLinkFacet
86
+
87
+ - $id: https://wikiba.se/ontology#Statement
88
+ iolanta:hasInstanceFacet:
89
+ $id: python://iolanta.facets.wikibase_statement_title.WikibaseStatementTitle
90
+ $: Title
91
+ →: https://iolanta.tech/datatypes/title
92
+
93
+ - $id: rdfs:Class
94
+ iolanta:hasInstanceFacet:
95
+ $id: python://iolanta.facets.textual_class.Class
96
+ $: Instances
97
+ →: https://iolanta.tech/cli/textual
98
+ ⪯:
99
+ - python://iolanta.facets.textual_browser.TextualBrowserFacet
100
+ - python://iolanta.facets.textual_default.InverseProperties
101
+
102
+ - $id: owl:Ontology
103
+ iolanta:hasInstanceFacet:
104
+ $id: python://iolanta.facets.textual_ontology.OntologyFacet
105
+ $: Ontology
106
+ →: https://iolanta.tech/cli/textual
107
+ ⪯: python://iolanta.facets.textual_default.TextualDefaultFacet
108
+
109
+ - $id: https://iolanta.tech/datatypes/textual-graph-triples
110
+ $: Graph Triples
111
+ iolanta:hasDefaultFacet:
112
+ $id: python://iolanta.facets.textual_graph_triples.GraphTriplesFacet
113
+ →: https://iolanta.tech/datatypes/textual-graph-triples
114
+
115
+ - $id: https://iolanta.tech/qname
116
+ iolanta:hasDefaultFacet:
117
+ $id: python://iolanta.facets.qname.QNameFacet
118
+
119
+ - $id: python://iolanta.facets.textual_property_pairs_table.TextualPropertyPairsTableFacet
120
+ $: Subjects → Objects
121
+ →: https://iolanta.tech/cli/textual
122
+ ↦: ASK WHERE { ?subject $this ?object }
123
+
124
+ - $id: python://iolanta.facets.textual_class.Class
125
+ ↦:
126
+ - ASK WHERE { ?instance a $this }
127
+ - ASK WHERE { $this rdfs:subClassOf ?superclass }
@@ -20,6 +20,11 @@ class Facet(Generic[FacetOutput]):
20
20
  iolanta: 'iolanta.Iolanta' = field(repr=False)
21
21
  as_datatype: Optional[NotLiteralNode] = None
22
22
 
23
+ @property
24
+ def this(self) -> NotLiteralNode:
25
+ """This node."""
26
+ return self.iri
27
+
23
28
  @property
24
29
  def stored_queries_path(self) -> Path:
25
30
  """Construct directory for stored queries for this facet."""
@@ -15,7 +15,16 @@ class FoundRow(TypedDict):
15
15
  """Facet and datatype to render an IRI."""
16
16
 
17
17
  facet: NotLiteralNode
18
- as_datatype: NotLiteralNode
18
+ output_datatype: NotLiteralNode
19
+
20
+
21
+ GET_QUERY_TO_FACET = """
22
+ SELECT ?facet ?match WHERE {
23
+ ?facet
24
+ iolanta:matches ?match ;
25
+ iolanta:outputs $as_datatype .
26
+ }
27
+ """
19
28
 
20
29
 
21
30
  def reorder_rows_by_facet_preferences( # noqa: WPS214, WPS210
@@ -103,6 +112,27 @@ class FacetFinder: # noqa: WPS214
103
112
  key=self.row_sorter_by_output_datatype,
104
113
  )
105
114
 
115
+ def by_sparql(self) -> Iterable[FoundRow]:
116
+ """Determine facet by SHACL shape of the data."""
117
+ if not isinstance(self.node, URIRef):
118
+ return
119
+
120
+ rows = self.iolanta.query(
121
+ GET_QUERY_TO_FACET,
122
+ as_datatype=self.as_datatype,
123
+ )
124
+
125
+ query_to_facet = {
126
+ row['match']: row['facet']
127
+ for row in rows
128
+ }
129
+
130
+ for query, facet in query_to_facet.items():
131
+ # TODO: Verify that `query` is an ASK query
132
+ is_matching = self.iolanta.query(query, this=self.node)
133
+ if is_matching:
134
+ yield FoundRow(facet=facet, output_datatype=self.as_datatype)
135
+
106
136
  def by_prefix(self) -> Iterable[FoundRow]:
107
137
  """Determine facet by URL prefix.
108
138
 
@@ -254,6 +284,7 @@ class FacetFinder: # noqa: WPS214
254
284
 
255
285
  def _found_facets(self) -> Iterable[FoundRow]:
256
286
  """Compose a stream of all possible facet choices."""
287
+ yield from self.by_sparql()
257
288
  yield from self.by_prefix()
258
289
  yield from self.by_datatype()
259
290
  yield from self.by_facet()
@@ -6,7 +6,7 @@ import funcy
6
6
  from rich.console import RenderResult
7
7
  from rich.text import Text
8
8
  from textual.binding import Binding, BindingType
9
- from textual.containers import Vertical
9
+ from textual.containers import Vertical # removed unused VerticalScroll (F401)
10
10
  from textual.reactive import Reactive
11
11
  from textual.widget import Widget
12
12
  from textual.widgets import Label, ListItem, ListView
@@ -70,10 +70,8 @@ class InstancesList(ListView): # noqa: WPS214
70
70
 
71
71
  DEFAULT_CSS = """
72
72
  InstancesList {
73
- height: auto;
74
-
73
+ height: 1fr;
75
74
  layout: vertical;
76
- overflow-x: hidden;
77
75
  overflow-y: auto;
78
76
  }
79
77
  """
@@ -121,31 +119,6 @@ class InstancesList(ListView): # noqa: WPS214
121
119
  parent_class=self.parent_class,
122
120
  )
123
121
 
124
- def on_list_view_highlighted(self):
125
- """
126
- Find out whether the last item of the list is highlighted.
127
-
128
- If yes then add more elements.
129
- """
130
- if not self._nodes or (self.index >= len(self._nodes) - 1):
131
- new_instance_items = list(self.stream_instance_items_chunk())
132
- self.run_worker(
133
- functools.partial(
134
- self.render_newly_added_instances,
135
- new_instance_items,
136
- ),
137
- thread=True,
138
- )
139
-
140
- # #126 `extend()` here will lead to freeze in some situations
141
- for new_item in new_instance_items:
142
- self.append(new_item)
143
-
144
- def on_list_item__child_clicked(self) -> None: # noqa: WPS116
145
- """Navigate on click."""
146
- # FIXME if we call `action_goto()` here we'll navigate to the item that
147
- # was selected _prior_ to this click.
148
-
149
122
  def action_goto(self):
150
123
  """Navigate."""
151
124
  self.app.action_goto(self.highlighted_child.node)
@@ -160,6 +133,32 @@ class InstancesList(ListView): # noqa: WPS214
160
133
  ),
161
134
  )
162
135
 
136
+ def compose(self):
137
+ """Fill in the instances."""
138
+ for instance in self.instances: # noqa: WPS526
139
+ yield InstanceItem(
140
+ node=instance,
141
+ parent_class=self.parent_class,
142
+ )
143
+
144
+ def render_instances(self):
145
+ """Render all instances."""
146
+ for instance_item in self.children:
147
+ self.app.call_from_thread(
148
+ instance_item.update,
149
+ self.app.iolanta.render(
150
+ instance_item.node,
151
+ as_datatype=DATATYPES.title,
152
+ ),
153
+ )
154
+
155
+ def on_mount(self):
156
+ """Render the first chunk of instances."""
157
+ self.run_worker(
158
+ self.render_instances,
159
+ thread=True,
160
+ )
161
+
163
162
 
164
163
  class Bottom(Label):
165
164
  """Label below the instances list."""
@@ -198,29 +197,19 @@ class Class(Facet[Widget]):
198
197
  We have to stop if a subsequent attempt returns no results. That's why
199
198
  we can't use `funcy.distinct()` or something similar.
200
199
  """
201
- known_instances: set[NotLiteralNode] = set()
202
- while True:
203
- instances = set(
204
- funcy.pluck(
205
- 'instance',
206
- self.stored_query('instances.sparql', iri=self.iri),
207
- ),
208
- ).difference(known_instances)
209
-
210
- if not instances:
211
- return
212
-
213
- yield from instances
214
-
215
- known_instances.update(instances)
200
+ return set(
201
+ funcy.pluck(
202
+ 'instance',
203
+ self.stored_query('instances.sparql', iri=self.iri),
204
+ ),
205
+ )
216
206
 
217
207
  def show(self) -> Widget:
218
208
  """Render the instances list."""
219
209
  return InstancesBody(
220
210
  PageTitle(self.iri),
221
211
  InstancesList(
222
- instances=self.stream_instances(),
212
+ instances=list(self.stream_instances()),
223
213
  parent_class=self.iri,
224
214
  ),
225
- Bottom('Select the last element to try loading more instances.'),
226
215
  )
@@ -1,5 +1,5 @@
1
1
  SELECT ?instance WHERE {
2
- ?instance rdf:type $iri .
2
+ ?instance a $iri .
3
3
 
4
4
  FILTER(!isLiteral(?instance)) .
5
5
  } ORDER BY ?instance
@@ -0,0 +1,133 @@
1
+ from collections import defaultdict
2
+ from typing import Iterable
3
+
4
+ import funcy
5
+ from rdflib import Literal, Node
6
+ from rich.style import Style
7
+ from rich.text import Text
8
+ from textual.containers import Vertical
9
+ from textual.coordinate import Coordinate
10
+ from textual.widgets import DataTable
11
+
12
+ from iolanta import Facet
13
+ from iolanta.facets.page_title import PageTitle
14
+ from iolanta.models import NotLiteralNode
15
+ from iolanta.namespaces import DATATYPES
16
+ from iolanta.widgets.mixin import IolantaWidgetMixin
17
+
18
+ PAIRS_QUERY = """
19
+ SELECT ?subject ?object WHERE {
20
+ ?subject $this ?object .
21
+ } ORDER BY ?subject ?object
22
+ """
23
+
24
+
25
+ class PairsTable(IolantaWidgetMixin, DataTable):
26
+ """Render subject → object properties as a table with two columns."""
27
+
28
+ BINDINGS = [
29
+ ('enter', 'goto', 'Goto'),
30
+ ]
31
+
32
+ def __init__(self, pairs: Iterable[tuple[NotLiteralNode, Node]]):
33
+ """Construct."""
34
+ super().__init__(show_header=False, cell_padding=1)
35
+ self.pairs = list(pairs)
36
+
37
+ def render_human_readable_cells(self):
38
+ """Replace the cells with their human readable titles."""
39
+ terms_and_coordinates = sorted(
40
+ self.node_to_coordinates.items(),
41
+ key=lambda node_and_coordinates_pair: len(
42
+ node_and_coordinates_pair[1],
43
+ ),
44
+ reverse=True,
45
+ )
46
+
47
+ for term, coordinates in terms_and_coordinates:
48
+ title = self.iolanta.render(term, as_datatype=DATATYPES.title)
49
+ for coordinate in coordinates:
50
+ self.app.call_from_thread(
51
+ self.update_cell_at,
52
+ coordinate,
53
+ value=Text(title, no_wrap=False),
54
+ update_width=False,
55
+ )
56
+
57
+ @funcy.cached_property
58
+ @funcy.post_processing(dict)
59
+ def coordinate_to_node(self):
60
+ """Return a mapping of coordinates to their corresponding nodes."""
61
+ for row_number, (subject, object_term) in enumerate(self.pairs):
62
+ yield Coordinate(row_number, 0), subject
63
+ yield Coordinate(row_number, 1), object_term
64
+
65
+ @funcy.cached_property
66
+ def node_to_coordinates(self) -> defaultdict[Node, list[Coordinate]]:
67
+ """Map node to coordinates where it appears."""
68
+ node_to_coordinate = [
69
+ (node, coordinate)
70
+ for coordinate, node in self.coordinate_to_node.items()
71
+ ]
72
+ return funcy.group_values(node_to_coordinate)
73
+
74
+ def format_as_loading(self, node: Node) -> Text:
75
+ """Intermediate version of a value while it is loading."""
76
+ if isinstance(node, Literal):
77
+ node_text = f'⌛ {node}'
78
+ else:
79
+ node_text = self.iolanta.node_as_qname(node)
80
+ node_text = f'⌛ {node_text}'
81
+
82
+ return Text(
83
+ node_text,
84
+ style=Style(dim=True),
85
+ no_wrap=False,
86
+ )
87
+
88
+ def on_mount(self):
89
+ """Fill the table and start rendering."""
90
+ self.add_columns('Subject', 'Object')
91
+ self.cell_padding = 1
92
+
93
+ for subject, object_term in self.pairs:
94
+ self.add_row(
95
+ self.format_as_loading(subject),
96
+ self.format_as_loading(object_term),
97
+ )
98
+
99
+ self.run_worker(
100
+ self.render_human_readable_cells,
101
+ thread=True,
102
+ )
103
+
104
+ def action_goto(self):
105
+ """Navigate to the selected node."""
106
+ if self.cursor_coordinate:
107
+ node = self.coordinate_to_node.get(self.cursor_coordinate)
108
+ if node is not None:
109
+ self.app.action_goto(node)
110
+
111
+
112
+ class TextualPropertyPairsTableFacet(Facet):
113
+ """Render a table of subject → object pairs for a property."""
114
+
115
+ def show(self):
116
+ """Construct the table."""
117
+ rows = self.query(
118
+ PAIRS_QUERY,
119
+ this=self.this,
120
+ )
121
+
122
+ return Vertical(
123
+ PageTitle(
124
+ self.this,
125
+ extra='— Subjects & Objects connected by this property',
126
+ ),
127
+ PairsTable(
128
+ [
129
+ (row['subject'], row['object'])
130
+ for row in rows
131
+ ],
132
+ ),
133
+ )
@@ -28,7 +28,6 @@ from yaml_ld.errors import YAMLLDError
28
28
 
29
29
  from iolanta import entry_points, namespaces
30
30
  from iolanta.conversions import path_to_iri
31
- from iolanta.cyberspace.processor import normalize_term
32
31
  from iolanta.errors import UnresolvedIRI
33
32
  from iolanta.facets.errors import FacetError
34
33
  from iolanta.facets.facet import Facet
@@ -53,6 +52,7 @@ from iolanta.query_result import (
53
52
  format_query_bindings,
54
53
  )
55
54
  from iolanta.resolvers.python_import import PythonImportResolver
55
+ from iolanta.sparqlspace.processor import normalize_term
56
56
 
57
57
 
58
58
  class LoggerProtocol(Protocol):
@@ -161,7 +161,7 @@ class Iolanta: # noqa: WPS214
161
161
  try:
162
162
  sparql_result: SPARQLResult = self.graph.query(
163
163
  query_text,
164
- processor='cyberspace',
164
+ processor='sparqlspace',
165
165
  initBindings=kwargs,
166
166
  )
167
167
  except ParseException as err:
@@ -259,7 +259,7 @@ class Iolanta: # noqa: WPS214
259
259
  )
260
260
 
261
261
  if not quads:
262
- self.logger.warning(f'{source_file} | No data found')
262
+ self.logger.info(f'{source_file} | No data found')
263
263
  continue
264
264
 
265
265
  quad_tuples = [
@@ -73,13 +73,6 @@ def parse_quads(
73
73
  graph,
74
74
  )
75
75
 
76
- yield Quad(
77
- graph_name,
78
- RDF.type,
79
- IOLANTA.Graph,
80
- IOLANTA.meta,
81
- )
82
-
83
76
  for quad in quads:
84
77
  try:
85
78
  yield Quad(
@@ -0,0 +1,64 @@
1
+ from enum import StrEnum
2
+ from typing import Annotated
3
+
4
+ import rich
5
+ from rdflib import Node
6
+ from rdflib.query import Result
7
+ from rich.table import Table
8
+ from typer import Option, Typer
9
+
10
+ from iolanta.sparqlspace.sparqlspace import SPARQLSpace
11
+
12
+ app = Typer()
13
+
14
+
15
+ class OutputFormat(StrEnum):
16
+ """Output formats for the query command."""
17
+
18
+ CSV = 'csv' # noqa: WPS115
19
+ JSON = 'json' # noqa: WPS115
20
+ TABLE = 'table' # noqa: WPS115
21
+
22
+
23
+ def _format_node(node: Node):
24
+ return node
25
+
26
+
27
+ def _format_result(query_result: Result, output_format: OutputFormat):
28
+ match output_format:
29
+ case OutputFormat.CSV:
30
+ return query_result.serialize(format='csv').decode()
31
+
32
+ case OutputFormat.JSON:
33
+ return query_result.serialize(format='json').decode()
34
+
35
+ case OutputFormat.TABLE:
36
+ table = Table(*query_result.vars)
37
+ for row in query_result:
38
+ table.add_row(
39
+ *[
40
+ _format_node(node)
41
+ for node in row
42
+ ],
43
+ )
44
+ return table
45
+
46
+ raise NotImplementedError(f'Output format {output_format} not implemented.')
47
+
48
+
49
+ @app.command(name='query')
50
+ def query_command(
51
+ query: str,
52
+ output_format: Annotated[
53
+ OutputFormat,
54
+ Option(help='Output format.'),
55
+ ] = OutputFormat.TABLE,
56
+ ):
57
+ """Execute a SPARQL query."""
58
+ query_result = SPARQLSpace().query(query)
59
+ rich.print(
60
+ _format_result(
61
+ query_result=query_result,
62
+ output_format=output_format,
63
+ ),
64
+ )