snapquery 0.0.2__tar.gz → 0.0.4__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.
- {snapquery-0.0.2 → snapquery-0.0.4}/PKG-INFO +4 -2
- {snapquery-0.0.2 → snapquery-0.0.4}/pyproject.toml +4 -2
- snapquery-0.0.4/snapquery/__init__.py +1 -0
- snapquery-0.0.4/snapquery/scholia.py +49 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/snapquery/snapquery_cmd.py +25 -8
- {snapquery-0.0.2 → snapquery-0.0.4}/snapquery/snapquery_core.py +238 -101
- snapquery-0.0.4/snapquery/snapquery_view.py +170 -0
- snapquery-0.0.4/snapquery/snapquery_webserver.py +214 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/snapquery/version.py +3 -2
- snapquery-0.0.4/tests/test_cmdline.py +75 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/tests/test_endpoints.py +1 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/tests/test_namedqueries.py +4 -2
- {snapquery-0.0.2 → snapquery-0.0.4}/tests/test_restful_api.py +25 -9
- snapquery-0.0.4/tests/test_scholia.py +26 -0
- snapquery-0.0.4/tests/test_wd_query_parsing.py +90 -0
- snapquery-0.0.2/snapquery/__init__.py +0 -1
- snapquery-0.0.2/snapquery/snapquery_webserver.py +0 -106
- {snapquery-0.0.2 → snapquery-0.0.4}/.github/workflows/build.yml +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/.github/workflows/upload-to-pypi.yml +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/.gitignore +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/.project +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/.pydevproject +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/LICENSE +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/README.md +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/scripts/blackisort +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/scripts/install +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/scripts/test +0 -0
- {snapquery-0.0.2 → snapquery-0.0.4}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: snapquery
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: snapquery: Introduce Named Queries and Named Query Middleware to wikidata
|
|
5
5
|
Project-URL: Home, https://github.com/WolfgangFahl/snapquery
|
|
6
6
|
Project-URL: Documentation, https://wiki.bitplan.com/index.php/snapquery
|
|
@@ -25,7 +25,9 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
25
25
|
Classifier: Topic :: Software Development :: User Interfaces
|
|
26
26
|
Requires-Python: >=3.9
|
|
27
27
|
Requires-Dist: ngwidgets>=0.14.2
|
|
28
|
-
Requires-Dist: pylodstorage>=0.10.
|
|
28
|
+
Requires-Dist: pylodstorage>=0.10.5
|
|
29
|
+
Requires-Dist: requests
|
|
30
|
+
Requires-Dist: wikitextparser
|
|
29
31
|
Provides-Extra: test
|
|
30
32
|
Requires-Dist: green; extra == 'test'
|
|
31
33
|
Description-Content-Type: text/markdown
|
|
@@ -19,8 +19,10 @@ license = {text = "Apache-2.0"}
|
|
|
19
19
|
dependencies = [
|
|
20
20
|
# https://github.com/WolfgangFahl/nicegui_widgets
|
|
21
21
|
"ngwidgets>=0.14.2",
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
# https://pypi.org/project/pyLodStorage/
|
|
23
|
+
"pyLodStorage>=0.10.5",
|
|
24
|
+
"wikitextparser",
|
|
25
|
+
"requests"
|
|
24
26
|
]
|
|
25
27
|
|
|
26
28
|
requires-python = ">=3.9"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.4"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Created on 2024-05-04
|
|
3
|
+
|
|
4
|
+
@author: wf
|
|
5
|
+
"""
|
|
6
|
+
import requests
|
|
7
|
+
from snapquery.snapquery_core import NamedQueryManager
|
|
8
|
+
class ScholiaQueries:
|
|
9
|
+
"""
|
|
10
|
+
scholia queries
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def get(cls,db_path:str=None,debug:bool=False,limit:int=None):
|
|
15
|
+
nqm = NamedQueryManager.from_samples(db_path=db_path)
|
|
16
|
+
repository_url = "https://api.github.com/repos/WDscholia/scholia/contents/scholia/app/templates"
|
|
17
|
+
headers = {'Accept': 'application/vnd.github.v3+json'}
|
|
18
|
+
response = requests.get(repository_url, headers=headers)
|
|
19
|
+
response.raise_for_status() # Ensure we notice bad responses
|
|
20
|
+
|
|
21
|
+
exclude = ["author_topics", "event_proceedings", "publisher_editors", "works_topics"]
|
|
22
|
+
lod=[]
|
|
23
|
+
file_list_json=response.json()
|
|
24
|
+
for i,file_info in enumerate(file_list_json,start=1):
|
|
25
|
+
file_name = file_info['name']
|
|
26
|
+
if file_name.endswith('.sparql') and file_name[:-7] not in exclude:
|
|
27
|
+
file_response = requests.get(file_info['download_url'])
|
|
28
|
+
file_response.raise_for_status()
|
|
29
|
+
query_str = file_response.text
|
|
30
|
+
name=file_name[:-7]
|
|
31
|
+
record={
|
|
32
|
+
"namespace":"scholia",
|
|
33
|
+
"name": name,
|
|
34
|
+
"url": file_info['download_url'],
|
|
35
|
+
"title": name,
|
|
36
|
+
"sparql":query_str
|
|
37
|
+
}
|
|
38
|
+
lod.append(record)
|
|
39
|
+
if debug:
|
|
40
|
+
if i % 80 == 0:
|
|
41
|
+
print(f"{i}")
|
|
42
|
+
print(".", end='', flush=True)
|
|
43
|
+
if limit and len(lod)>=limit:
|
|
44
|
+
break
|
|
45
|
+
if debug:
|
|
46
|
+
print(f"found {len(lod)} scholia queries")
|
|
47
|
+
nqm.store(lod)
|
|
48
|
+
return nqm
|
|
49
|
+
|
|
@@ -3,10 +3,11 @@ Created on 2024-05-03
|
|
|
3
3
|
|
|
4
4
|
@author: wf
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
import sys
|
|
7
8
|
from argparse import ArgumentParser
|
|
8
9
|
|
|
9
|
-
from lodstorage.query import EndpointManager, Format
|
|
10
|
+
from lodstorage.query import EndpointManager, Format
|
|
10
11
|
from ngwidgets.cmd import WebserverCmd
|
|
11
12
|
|
|
12
13
|
from snapquery.snapquery_core import NamedQueryManager
|
|
@@ -34,7 +35,7 @@ class SnapQueryCmd(WebserverCmd):
|
|
|
34
35
|
"-en",
|
|
35
36
|
"--endpointName",
|
|
36
37
|
default="wikidata",
|
|
37
|
-
help=
|
|
38
|
+
help="Name of the endpoint to use for queries - use --listEndpoints to list available endpoints",
|
|
38
39
|
)
|
|
39
40
|
parser.add_argument(
|
|
40
41
|
"-le",
|
|
@@ -42,6 +43,15 @@ class SnapQueryCmd(WebserverCmd):
|
|
|
42
43
|
action="store_true",
|
|
43
44
|
help="show the list of available endpoints",
|
|
44
45
|
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--limit", type=int, default=None, help="set limit parameter of query"
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"--namespace",
|
|
51
|
+
type=str,
|
|
52
|
+
default="wikidata-examples",
|
|
53
|
+
help="namespace to filter queries",
|
|
54
|
+
)
|
|
45
55
|
parser.add_argument("-qn", "--queryName", help="run a named query")
|
|
46
56
|
parser.add_argument("-f", "--format", type=Format, choices=list(Format))
|
|
47
57
|
return parser
|
|
@@ -60,15 +70,22 @@ class SnapQueryCmd(WebserverCmd):
|
|
|
60
70
|
print(endpoint)
|
|
61
71
|
handled = True # Operation handled
|
|
62
72
|
elif self.args.queryName is not None:
|
|
63
|
-
nqm = NamedQueryManager()
|
|
73
|
+
nqm = NamedQueryManager.from_samples()
|
|
74
|
+
namespace = self.args.namespace
|
|
64
75
|
name = self.args.queryName
|
|
65
76
|
endpoint_name = self.args.endpointName
|
|
66
77
|
r_format = self.args.format
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
limit = self.args.limit
|
|
79
|
+
qb = nqm.get_query(
|
|
80
|
+
name=name, namespace=namespace, endpoint_name=endpoint_name, limit=limit
|
|
81
|
+
)
|
|
82
|
+
if r_format==Format.raw:
|
|
83
|
+
formatted_result=qb.raw_query()
|
|
84
|
+
else:
|
|
85
|
+
qlod = qb.get_lod()
|
|
86
|
+
formatted_result = qb.format_result(qlod=qlod, r_format=r_format)
|
|
87
|
+
print(formatted_result)
|
|
88
|
+
return handled
|
|
72
89
|
|
|
73
90
|
|
|
74
91
|
def main(argv: list = None):
|
|
@@ -3,8 +3,11 @@ Created on 2024-05-03
|
|
|
3
3
|
|
|
4
4
|
@author: wf
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
9
|
+
import re
|
|
10
|
+
import requests
|
|
8
11
|
from dataclasses import field
|
|
9
12
|
from pathlib import Path
|
|
10
13
|
from typing import Any, Dict, List, Optional
|
|
@@ -14,6 +17,7 @@ from lodstorage.query import Endpoint, EndpointManager, Format, Query
|
|
|
14
17
|
from lodstorage.sparql import SPARQL
|
|
15
18
|
from lodstorage.sql import SQLDB, EntityInfo
|
|
16
19
|
from lodstorage.yamlable import lod_storable
|
|
20
|
+
from ngwidgets.widgets import Link
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
@lod_storable
|
|
@@ -36,13 +40,15 @@ class NamedQuery:
|
|
|
36
40
|
namespace: str
|
|
37
41
|
# name/id
|
|
38
42
|
name: str
|
|
39
|
-
# one line title
|
|
40
|
-
title: str
|
|
41
|
-
# multiline description
|
|
42
|
-
description: str
|
|
43
43
|
# sparql query (to be hidden later)
|
|
44
44
|
sparql: str
|
|
45
|
-
|
|
45
|
+
# the url of the source code of the query
|
|
46
|
+
url: Optional[str]=None
|
|
47
|
+
# one line title
|
|
48
|
+
title: Optional[str]=None
|
|
49
|
+
# multiline description
|
|
50
|
+
description: Optional[str]=None
|
|
51
|
+
|
|
46
52
|
def __post_init__(self):
|
|
47
53
|
"""
|
|
48
54
|
Post-initialization processing to construct a unique identifier for the query
|
|
@@ -50,6 +56,166 @@ class NamedQuery:
|
|
|
50
56
|
"""
|
|
51
57
|
self.query_id = f"{self.namespace}.{self.name}"
|
|
52
58
|
|
|
59
|
+
def as_link(self) -> str:
|
|
60
|
+
"""
|
|
61
|
+
get me as a link
|
|
62
|
+
"""
|
|
63
|
+
url = f"/query/{self.namespace}/{self.name}"
|
|
64
|
+
text = self.name
|
|
65
|
+
tooltip = "query details"
|
|
66
|
+
link = Link.create(url, text, tooltip)
|
|
67
|
+
return link
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def from_record(cls, record: Dict) -> "NamedQuery":
|
|
71
|
+
"""
|
|
72
|
+
Class method to instantiate NamedQuery from a dictionary record.
|
|
73
|
+
"""
|
|
74
|
+
return cls(
|
|
75
|
+
namespace=record["namespace"],
|
|
76
|
+
name=record["name"],
|
|
77
|
+
title=record["title"],
|
|
78
|
+
url=record["url"],
|
|
79
|
+
description=record["description"],
|
|
80
|
+
sparql=record["sparql"],
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def as_record(self)->Dict:
|
|
84
|
+
record = {
|
|
85
|
+
"namespace": self.namespace,
|
|
86
|
+
"name": self.name,
|
|
87
|
+
"url": self.url,
|
|
88
|
+
"title": self.title,
|
|
89
|
+
"description": self.description,
|
|
90
|
+
"sparql": self.sparql,
|
|
91
|
+
}
|
|
92
|
+
return record
|
|
93
|
+
|
|
94
|
+
def as_viewrecord(self) -> Dict:
|
|
95
|
+
"""
|
|
96
|
+
Return a dictionary representing the NamedQuery with keys ordered as Name, Namespace, Title, Description.
|
|
97
|
+
"""
|
|
98
|
+
return {
|
|
99
|
+
"name": self.as_link(),
|
|
100
|
+
"namespace": self.namespace,
|
|
101
|
+
"title": self.title,
|
|
102
|
+
"description": self.description,
|
|
103
|
+
"url": self.url
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class QueryBundle:
|
|
108
|
+
"""
|
|
109
|
+
Bundles a named query, a query, and an endpoint into a single manageable object, facilitating the execution of SPARQL queries.
|
|
110
|
+
|
|
111
|
+
Attributes:
|
|
112
|
+
named_query (NamedQuery): The named query object, which includes metadata about the query.
|
|
113
|
+
query (Query): The actual query object that contains the SPARQL query string.
|
|
114
|
+
endpoint (Endpoint): The endpoint object where the SPARQL query should be executed.
|
|
115
|
+
sparql (SPARQL): A SPARQL service object initialized with the endpoint URL.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
def __init__(self, named_query: NamedQuery, query: Query, endpoint: Endpoint):
|
|
119
|
+
"""
|
|
120
|
+
Initializes a new instance of the QueryBundle class.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
named_query (NamedQuery): An instance of NamedQuery that provides a named reference to the query.
|
|
124
|
+
query (Query): An instance of Query containing the SPARQL query string.
|
|
125
|
+
endpoint (Endpoint): An instance of Endpoint representing the SPARQL endpoint URL.
|
|
126
|
+
"""
|
|
127
|
+
self.named_query = named_query
|
|
128
|
+
self.query = query
|
|
129
|
+
self.endpoint = endpoint
|
|
130
|
+
self.sparql = SPARQL(endpoint.endpoint)
|
|
131
|
+
|
|
132
|
+
def raw_query(self,resultFormat, mime_type:str=None, timeout:float=10.0):
|
|
133
|
+
"""
|
|
134
|
+
returns raw result of the endpoint
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
resultFormat(str): format of the result
|
|
138
|
+
mime_type(str): mime_type to use (if any)
|
|
139
|
+
timeout(float): timeout in seconds
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
raw result of the query
|
|
143
|
+
"""
|
|
144
|
+
params = {"query": self.query.query, "format": resultFormat}
|
|
145
|
+
payload = {}
|
|
146
|
+
if mime_type:
|
|
147
|
+
headers = {"Accept": mime_type}
|
|
148
|
+
else:
|
|
149
|
+
headers = {}
|
|
150
|
+
endpoint_url = self.endpoint.endpoint
|
|
151
|
+
method = self.endpoint.method
|
|
152
|
+
response = requests.request(
|
|
153
|
+
method, endpoint_url, headers=headers, data=payload, params=params,timeout=timeout
|
|
154
|
+
)
|
|
155
|
+
return response.text
|
|
156
|
+
|
|
157
|
+
def get_lod(self) -> List[dict]:
|
|
158
|
+
"""
|
|
159
|
+
Executes the stored query using the SPARQL service and returns the results as a list of dictionaries.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
List[dict]: A list where each dictionary represents a row of results from the SPARQL query.
|
|
163
|
+
"""
|
|
164
|
+
lod = self.sparql.queryAsListOfDicts(self.query.query)
|
|
165
|
+
return lod
|
|
166
|
+
|
|
167
|
+
def format_result(
|
|
168
|
+
self,
|
|
169
|
+
qlod: List[Dict[str, Any]] = None,
|
|
170
|
+
r_format: Format = Format.json,
|
|
171
|
+
) -> Optional[str]:
|
|
172
|
+
"""
|
|
173
|
+
Formats the query results based on the specified format and prints them.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
qlod (List[Dict[str, Any]]): The list of dictionaries that represent the query results.
|
|
177
|
+
query (Query): The query object which contains details like the endpoint and the database.
|
|
178
|
+
r_format(Format): The format in which to print the results.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Optional[str]: The formatted string representation of the query results, or None if printed directly.
|
|
182
|
+
"""
|
|
183
|
+
if qlod is None:
|
|
184
|
+
qlod = self.get_lod()
|
|
185
|
+
if r_format is None:
|
|
186
|
+
r_format = Format.json
|
|
187
|
+
if r_format == Format.csv:
|
|
188
|
+
csv_output = CSV.toCSV(qlod)
|
|
189
|
+
return csv_output
|
|
190
|
+
elif r_format in [Format.latex, Format.github, Format.mediawiki, Format.html]:
|
|
191
|
+
doc = self.query.documentQueryResult(
|
|
192
|
+
qlod, tablefmt=str(r_format), floatfmt=".0f"
|
|
193
|
+
)
|
|
194
|
+
return doc.asText()
|
|
195
|
+
elif r_format == Format.json:
|
|
196
|
+
return json.dumps(qlod, indent=2, sort_keys=True, default=str)
|
|
197
|
+
return None # In case no format is matched or needed
|
|
198
|
+
|
|
199
|
+
def set_limit(self, limit: int = None):
|
|
200
|
+
"""
|
|
201
|
+
set the limit of my query
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
limit(int): the limit to set - default: None
|
|
205
|
+
"""
|
|
206
|
+
if limit:
|
|
207
|
+
sparql_query = self.query.query
|
|
208
|
+
# @TODO - this is too naive for cases where
|
|
209
|
+
# there are SPARQL elements hat have a "limit" in the name e.g. "height_limit"
|
|
210
|
+
# or if there is a LIMIT in a subquery
|
|
211
|
+
if "limit" in sparql_query or "LIMIT" in sparql_query:
|
|
212
|
+
sparql_query = re.sub(
|
|
213
|
+
r"(limit|LIMIT)\s+(\d+)", f"LIMIT {limit}", sparql_query
|
|
214
|
+
)
|
|
215
|
+
else:
|
|
216
|
+
sparql_query += f"\nLIMIT {limit}"
|
|
217
|
+
self.query.query = sparql_query
|
|
218
|
+
|
|
53
219
|
|
|
54
220
|
class NamedQueryManager:
|
|
55
221
|
"""
|
|
@@ -72,7 +238,7 @@ class NamedQueryManager:
|
|
|
72
238
|
if db_path is None:
|
|
73
239
|
db_path = NamedQueryManager.get_cache_path()
|
|
74
240
|
self.debug = debug
|
|
75
|
-
self.sql_db = SQLDB(dbname=db_path,check_same_thread=False, debug=debug)
|
|
241
|
+
self.sql_db = SQLDB(dbname=db_path, check_same_thread=False, debug=debug)
|
|
76
242
|
self.endpoints = EndpointManager.getEndpoints(lang="sparql")
|
|
77
243
|
|
|
78
244
|
@classmethod
|
|
@@ -103,23 +269,33 @@ class NamedQueryManager:
|
|
|
103
269
|
nqm = NamedQueryManager(debug=debug)
|
|
104
270
|
path_obj = Path(db_path)
|
|
105
271
|
if not path_obj.exists() or path_obj.stat().st_size == 0:
|
|
106
|
-
|
|
107
|
-
list_of_records = []
|
|
108
|
-
for _query_name, named_query in sample_queries.items():
|
|
109
|
-
record = {
|
|
110
|
-
"namespace": named_query.namespace,
|
|
111
|
-
"name": named_query.name,
|
|
112
|
-
"title": named_query.title,
|
|
113
|
-
"description": named_query.description,
|
|
114
|
-
"sparql": named_query.sparql,
|
|
115
|
-
}
|
|
116
|
-
list_of_records.append(record)
|
|
272
|
+
sample_records=cls.get_sample_records()
|
|
117
273
|
entityInfo = EntityInfo(
|
|
118
|
-
|
|
274
|
+
sample_records, name="NamedQuery", primaryKey="query_id"
|
|
119
275
|
)
|
|
120
|
-
nqm.sql_db.createTable(
|
|
121
|
-
nqm.sql_db.store(
|
|
276
|
+
nqm.sql_db.createTable(sample_records, "NamedQuery", withDrop=True)
|
|
277
|
+
nqm.sql_db.store(sample_records, entityInfo)
|
|
122
278
|
return nqm
|
|
279
|
+
|
|
280
|
+
def store(self,lod):
|
|
281
|
+
"""
|
|
282
|
+
store the given list of dicts
|
|
283
|
+
"""
|
|
284
|
+
sample_records=NamedQueryManager.get_sample_records()
|
|
285
|
+
entityInfo = EntityInfo(
|
|
286
|
+
sample_records, name="NamedQuery", primaryKey="query_id"
|
|
287
|
+
)
|
|
288
|
+
self.sql_db.store(lod, entityInfo,fixNone=True,replace=True)
|
|
289
|
+
|
|
290
|
+
@classmethod
|
|
291
|
+
def get_sample_records(cls):
|
|
292
|
+
sample_queries = cls.get_samples()
|
|
293
|
+
list_of_records = []
|
|
294
|
+
for _query_name, named_query in sample_queries.items():
|
|
295
|
+
record=named_query.as_record()
|
|
296
|
+
list_of_records.append(record)
|
|
297
|
+
return list_of_records
|
|
298
|
+
|
|
123
299
|
|
|
124
300
|
@classmethod
|
|
125
301
|
def get_samples(cls) -> dict[str, NamedQuery]:
|
|
@@ -130,6 +306,7 @@ class NamedQueryManager:
|
|
|
130
306
|
"wikidata-examples.cats": NamedQuery(
|
|
131
307
|
namespace="wikidata-examples",
|
|
132
308
|
name="cats",
|
|
309
|
+
url="https://www.wikidata.org/wiki/Wikidata:SPARQL_query_service/queries/examples#Cats",
|
|
133
310
|
title="Cats on Wikidata",
|
|
134
311
|
description="This query retrieves all items classified under 'house cat' (Q146) on Wikidata.",
|
|
135
312
|
sparql="""
|
|
@@ -143,6 +320,7 @@ WHERE {
|
|
|
143
320
|
"wikidata-examples.horses": NamedQuery(
|
|
144
321
|
namespace="wikidata-examples",
|
|
145
322
|
name="horses",
|
|
323
|
+
url="https://www.wikidata.org/wiki/Wikidata:SPARQL_query_service/queries/examples#Horses_(showing_some_info_about_them)",
|
|
146
324
|
title="Horses on Wikidata",
|
|
147
325
|
description="This query retrieves information about horses, including parents, gender, and approximate birth and death years.",
|
|
148
326
|
sparql="""
|
|
@@ -165,14 +343,20 @@ ORDER BY ?horse
|
|
|
165
343
|
}
|
|
166
344
|
return samples
|
|
167
345
|
|
|
168
|
-
def
|
|
169
|
-
|
|
170
|
-
name
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
346
|
+
def lookup(self, name: str, namespace: str) -> NamedQuery:
|
|
347
|
+
"""
|
|
348
|
+
lookup the named query for the given name and namespace
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
name (str): The name of the named query to execute.
|
|
353
|
+
namespace (str): The namespace of the named query, default is 'wikidata-examples'.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
NamedQuery: the named query
|
|
357
|
+
"""
|
|
358
|
+
sql_query = """SELECT
|
|
359
|
+
*
|
|
176
360
|
FROM
|
|
177
361
|
NamedQuery
|
|
178
362
|
WHERE
|
|
@@ -188,96 +372,49 @@ WHERE
|
|
|
188
372
|
msg = f"multiple entries ({query_count}) for name '{name}' and namespace '{namespace}'"
|
|
189
373
|
raise ValueError(msg)
|
|
190
374
|
|
|
191
|
-
|
|
192
|
-
|
|
375
|
+
record = query_records[0]
|
|
376
|
+
named_query = NamedQuery.from_record(record)
|
|
377
|
+
return named_query
|
|
193
378
|
|
|
194
|
-
def
|
|
379
|
+
def get_query(
|
|
195
380
|
self,
|
|
196
381
|
name: str,
|
|
197
382
|
namespace: str = "wikidata-examples",
|
|
198
383
|
endpoint_name: str = "wikidata",
|
|
199
|
-
|
|
384
|
+
limit: int = None,
|
|
385
|
+
) -> QueryBundle:
|
|
200
386
|
"""
|
|
201
|
-
|
|
387
|
+
get the query for the given parameters
|
|
202
388
|
|
|
203
389
|
Args:
|
|
204
390
|
name (str): The name of the named query to execute.
|
|
205
391
|
namespace (str): The namespace of the named query, default is 'wikidata-examples'.
|
|
206
392
|
endpoint_name (str): The name of the endpoint to send the SPARQL query to, default is 'wikidata'.
|
|
393
|
+
limit(int): the query limit (if any)
|
|
207
394
|
|
|
208
395
|
Returns:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
Raises:
|
|
212
|
-
ValueError: If no named query matches the given name and namespace.
|
|
213
|
-
Exception: If the SPARQL query execution fails or the endpoint returns an error.
|
|
396
|
+
QueryBundle: named_query, query and endpoint
|
|
214
397
|
"""
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if not endpoint_name in self.endpoints:
|
|
398
|
+
named_query = self.lookup(name, namespace)
|
|
399
|
+
if endpoint_name not in self.endpoints:
|
|
218
400
|
msg = f"Invalid endpoint {endpoint_name}"
|
|
219
401
|
ValueError(msg)
|
|
220
402
|
endpoint = self.endpoints.get(endpoint_name)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
r_format_str: str,
|
|
230
|
-
endpoint_name: str = "wikidata",
|
|
231
|
-
) -> Optional[str]:
|
|
232
|
-
"""
|
|
233
|
-
Formats the query results based on the specified format and prints them.
|
|
234
|
-
|
|
235
|
-
Args:
|
|
236
|
-
qlod (List[Dict[str, Any]]): The list of dictionaries that represent the query results.
|
|
237
|
-
query (Query): The query object which contains details like the endpoint and the database.
|
|
238
|
-
r_format_str: The format in which to print the results.
|
|
239
|
-
endpoint_name (str): The name of the endpoint to be used, defaults to "wikidata".
|
|
240
|
-
|
|
241
|
-
Returns:
|
|
242
|
-
Optional[str]: The formatted string representation of the query results, or None if printed directly.
|
|
243
|
-
"""
|
|
244
|
-
# Retrieve endpoint configuration using the provided endpoint name
|
|
403
|
+
sparql_query = named_query.sparql
|
|
404
|
+
query = Query(
|
|
405
|
+
name=name,
|
|
406
|
+
query=sparql_query,
|
|
407
|
+
lang="sparql",
|
|
408
|
+
endpoint=endpoint.endpoint,
|
|
409
|
+
limit=limit,
|
|
410
|
+
)
|
|
245
411
|
endpointConf = self.endpoints.get(endpoint_name, Endpoint.getDefault())
|
|
246
412
|
query.tryItUrl = endpointConf.website
|
|
247
413
|
query.database = endpointConf.database
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
qlod, tablefmt=str(r_format), floatfmt=".0f"
|
|
256
|
-
)
|
|
257
|
-
return doc.asText()
|
|
258
|
-
elif r_format == Format.json:
|
|
259
|
-
return json.dumps(qlod, indent=2, sort_keys=True, default=str)
|
|
260
|
-
return None # In case no format is matched or needed
|
|
261
|
-
|
|
262
|
-
def print_result(
|
|
263
|
-
self,
|
|
264
|
-
qlod: List[Dict[str, Any]],
|
|
265
|
-
query: Query,
|
|
266
|
-
r_format_str: str,
|
|
267
|
-
endpoint_name: str = "wikidata",
|
|
268
|
-
) -> None:
|
|
269
|
-
"""
|
|
270
|
-
Prints the formatted results of a query.
|
|
271
|
-
|
|
272
|
-
Args:
|
|
273
|
-
qlod (List[Dict[str, Any]]): The list of dictionaries that represent the query results.
|
|
274
|
-
query (Query): The query object which contains details such as the endpoint and the database.
|
|
275
|
-
r_format_str: The format in which to print the results.
|
|
276
|
-
endpoint_name (str): The name of the endpoint to be used, defaults to "wikidata".
|
|
277
|
-
|
|
278
|
-
Returns:
|
|
279
|
-
None
|
|
280
|
-
"""
|
|
281
|
-
result_str = self.format_result(qlod, query, r_format_str, endpoint_name)
|
|
282
|
-
if result_str is not None:
|
|
283
|
-
print(result_str)
|
|
414
|
+
query_bundle = QueryBundle(
|
|
415
|
+
named_query=named_query,
|
|
416
|
+
query=query,
|
|
417
|
+
endpoint=endpoint
|
|
418
|
+
)
|
|
419
|
+
query_bundle.set_limit(limit)
|
|
420
|
+
return query_bundle
|