snapquery 0.2.4__tar.gz → 0.2.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 (141) hide show
  1. {snapquery-0.2.4 → snapquery-0.2.5}/PKG-INFO +1 -1
  2. snapquery-0.2.5/snapquery/__init__.py +1 -0
  3. snapquery-0.2.5/snapquery/query_parameterizer.py +246 -0
  4. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/endpoints.yaml +19 -2
  5. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/version.py +1 -1
  6. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_cmdline.py +4 -2
  7. snapquery-0.2.5/tests/test_scholia.py +123 -0
  8. snapquery-0.2.4/snapquery/__init__.py +0 -1
  9. snapquery-0.2.4/tests/test_scholia.py +0 -246
  10. {snapquery-0.2.4 → snapquery-0.2.5}/.github/workflows/build.yml +0 -0
  11. {snapquery-0.2.4 → snapquery-0.2.5}/.github/workflows/upload-to-pypi.yml +0 -0
  12. {snapquery-0.2.4 → snapquery-0.2.5}/.gitignore +0 -0
  13. {snapquery-0.2.4 → snapquery-0.2.5}/.project +0 -0
  14. {snapquery-0.2.4 → snapquery-0.2.5}/.pydevproject +0 -0
  15. {snapquery-0.2.4 → snapquery-0.2.5}/CITATION.cff +0 -0
  16. {snapquery-0.2.4 → snapquery-0.2.5}/LICENSE +0 -0
  17. {snapquery-0.2.4 → snapquery-0.2.5}/README.md +0 -0
  18. {snapquery-0.2.4 → snapquery-0.2.5}/docs/index.md +0 -0
  19. {snapquery-0.2.4 → snapquery-0.2.5}/mkdocs.yml +0 -0
  20. {snapquery-0.2.4 → snapquery-0.2.5}/pyproject.toml +0 -0
  21. {snapquery-0.2.4 → snapquery-0.2.5}/scripts/blackisort +0 -0
  22. {snapquery-0.2.4 → snapquery-0.2.5}/scripts/doc +0 -0
  23. {snapquery-0.2.4 → snapquery-0.2.5}/scripts/fixdomain +0 -0
  24. {snapquery-0.2.4 → snapquery-0.2.5}/scripts/install +0 -0
  25. {snapquery-0.2.4 → snapquery-0.2.5}/scripts/qlv +0 -0
  26. {snapquery-0.2.4 → snapquery-0.2.5}/scripts/release +0 -0
  27. {snapquery-0.2.4 → snapquery-0.2.5}/scripts/restore_db +0 -0
  28. {snapquery-0.2.4 → snapquery-0.2.5}/scripts/test +0 -0
  29. {snapquery-0.2.4 → snapquery-0.2.5}/scripts/test_queries +0 -0
  30. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/authorization.py +0 -0
  31. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/basequeryview.py +0 -0
  32. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/ceurws.py +0 -0
  33. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/dblp.py +0 -0
  34. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/error_filter.py +0 -0
  35. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/execution.py +0 -0
  36. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/github_access.py +0 -0
  37. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/graph.py +0 -0
  38. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/models/__init__.py +0 -0
  39. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/models/person.py +0 -0
  40. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/models/sparql_components.py +0 -0
  41. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/mwlogin.py +0 -0
  42. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/namespace_stats_view.py +0 -0
  43. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/orcid.py +0 -0
  44. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/params_view.py +0 -0
  45. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/person_selector.py +0 -0
  46. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/pid.py +0 -0
  47. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/pid_lookup.py +0 -0
  48. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/prefix_merger.py +0 -0
  49. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/qlever.py +0 -0
  50. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/query_annotate.py +0 -0
  51. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/query_selector.py +0 -0
  52. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/query_set_cmd.py +0 -0
  53. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/query_set_tool.py +0 -0
  54. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/query_set_tool_view.py +0 -0
  55. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/resources/sparql_language.json +0 -0
  56. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/WikiProject_LSEThesisProject.wiki +0 -0
  57. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/ceur-ws.json +0 -0
  58. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/dblp.json +0 -0
  59. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/endpoints_optional.yaml +0 -0
  60. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/federated-query-examples.json +0 -0
  61. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/graphs.yaml +0 -0
  62. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/meta_query.yaml +0 -0
  63. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/orkg.json +0 -0
  64. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/qlever.json +0 -0
  65. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/qlever_performance.json +0 -0
  66. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/ceur-ws.yaml +0 -0
  67. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/dblp.yaml +0 -0
  68. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/federated-query-examples.yaml +0 -0
  69. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/hartig-eqs-2025-12-16.yaml +0 -0
  70. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/orkg.yaml +0 -0
  71. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/qlever-issues-2025-12-05.yaml +0 -0
  72. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/qlever.yaml +0 -0
  73. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/qlever_performance.yaml +0 -0
  74. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/scholia-2025-12-04.yaml +0 -0
  75. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/scholia-2025-12-16.yaml +0 -0
  76. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/scholia-qlever-2025-12-16.yaml +0 -0
  77. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/scholia.yaml +0 -0
  78. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/sib-2025-12-04.yaml +0 -0
  79. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/snapquery.yaml +0 -0
  80. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/wikidata-WikidataThesisToolkit.yaml +0 -0
  81. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/wikidata-WikidataThesisToolkitAlt.yaml +0 -0
  82. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/wikidata-examples-2025-12-04.yaml +0 -0
  83. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/wikidata-examples.yaml +0 -0
  84. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/wikidata-short-urls-2025-12-06.yaml +0 -0
  85. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query-sets/yaml/wikidata-short-urls.yaml +0 -0
  86. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/query_stats.yaml +0 -0
  87. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/scholia.json +0 -0
  88. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/snapquery.json +0 -0
  89. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/wikidata-WikidataThesisToolkit.json +0 -0
  90. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/wikidata-WikidataThesisToolkitAlt.json +0 -0
  91. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/wikidata-examples.json +0 -0
  92. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/samples/wikidata-short-urls.json +0 -0
  93. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/scholia.py +0 -0
  94. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/sib_sparql_examples.py +0 -0
  95. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/snapquery_cmd.py +0 -0
  96. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/snapquery_core.py +0 -0
  97. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/snapquery_view.py +0 -0
  98. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/snapquery_webserver.py +0 -0
  99. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/sparql_analyzer.py +0 -0
  100. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/stats_view.py +0 -0
  101. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/wd_page_query_extractor.py +0 -0
  102. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery/wd_short_url.py +0 -0
  103. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery.puml +0 -0
  104. {snapquery-0.2.4 → snapquery-0.2.5}/snapquery.yaml +0 -0
  105. {snapquery-0.2.4 → snapquery-0.2.5}/tests/__init__.py +0 -0
  106. {snapquery-0.2.4 → snapquery-0.2.5}/tests/action_stats.py +0 -0
  107. {snapquery-0.2.4 → snapquery-0.2.5}/tests/query_extractors/__init__.py +0 -0
  108. {snapquery-0.2.4 → snapquery-0.2.5}/tests/query_extractors/sparql_standard_queries.py +0 -0
  109. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/qlever/ask_unsupported.txt +0 -0
  110. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/qlever/invalid_literal.txt +0 -0
  111. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/qlever/invalid_regex_func.txt +0 -0
  112. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/qlever/memory_limit.txt +0 -0
  113. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/qlever/timeout.txt +0 -0
  114. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/triplydb/parser_error.txt +0 -0
  115. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/wdqs/duplicate_prefix.txt +0 -0
  116. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/wdqs/invalid_iri_declaration.txt +0 -0
  117. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/wdqs/invalid_sparql_syntax.txt +0 -0
  118. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/wdqs/timeout.txt +0 -0
  119. {snapquery-0.2.4 → snapquery-0.2.5}/tests/resources/error_messages/wdqs/undefined_prefix.txt +0 -0
  120. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_authorization.py +0 -0
  121. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_ceurws.py +0 -0
  122. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_endpoints.py +0 -0
  123. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_error_filter.py +0 -0
  124. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_graph.py +0 -0
  125. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_namedqueries.py +0 -0
  126. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_oauth.py +0 -0
  127. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_orcid.py +0 -0
  128. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_pids.py +0 -0
  129. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_prefix_merger.py +0 -0
  130. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_qlever.py +0 -0
  131. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_query_annotate.py +0 -0
  132. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_query_execution.py +0 -0
  133. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_query_name.py +0 -0
  134. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_query_set_tool.py +0 -0
  135. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_restful_api.py +0 -0
  136. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_scholia_q.py +0 -0
  137. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_sib_examples.py +0 -0
  138. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_sparql_analyzer.py +0 -0
  139. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_sparql_components.py +0 -0
  140. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_wd_query_parsing.py +0 -0
  141. {snapquery-0.2.4 → snapquery-0.2.5}/tests/test_wd_short_url.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snapquery
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Just Query wikidata and other SPARQL endpoints by query name - a frontend to Introduce Named Queries and Named Query Middleware to wikidata and other SPARQL endpoints
5
5
  Project-URL: Home, https://github.com/WolfgangFahl/snapquery
6
6
  Project-URL: Documentation, https://wiki.bitplan.com/index.php/snapquery
@@ -0,0 +1 @@
1
+ __version__ = "0.2.5"
@@ -0,0 +1,246 @@
1
+ """
2
+ Created on 2025-12-16
3
+
4
+ @author: wf
5
+ """
6
+ import os
7
+ import re
8
+ import textwrap
9
+ from typing import Optional, Dict, Any, List
10
+
11
+ from lodstorage.params import Param
12
+ from lodstorage.query import Query
13
+ from snapquery.snapquery_core import NamedQuerySet, NamedQuery
14
+
15
+
16
+ class QueryParameterizer:
17
+ """
18
+ Parameterizer for Scholia Query Sets.
19
+ Transforms Jinja2-based Scholia templates into standard
20
+ LoDStorage Parameterized Queries.
21
+ """
22
+
23
+ # Replacement for Scholia's label macro
24
+ LABEL_SERVICE = 'SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }'
25
+
26
+ # Defaults for specific Scholia Aspects
27
+ # derived from the prompt list
28
+ ASPECT_DEFAULTS = {
29
+ "author": "Q80", # Tim Berners-Lee
30
+ "authors": "Q80",
31
+ "award": "Q616568", # Turing Award
32
+ "catalogue": "Q2463660", # British Library catalogue
33
+ "chemical": "Q18216", # Aspirin
34
+ "cito": "Q1054942", # citation intent
35
+ "clinical": "Q12136", # Common Cold (disease context)
36
+ "complex": "Q417724", # Protein complex
37
+ "countries": "Q183", # Germany
38
+ "country": "Q183",
39
+ "dataset": "Q1172284", # Data set
40
+ "disease": "Q12136", # Common Cold
41
+ "event": "Q2020153", # WWW Conference
42
+ "gene": "Q7191", # p53
43
+ "index": "Q1", # Universe (generic fallback)
44
+ "language": "Q1860", # English
45
+ "lexeme": "Q151326", # Word
46
+ "license": "Q20007257", # CC BY 4.0
47
+ "location": "Q64", # Berlin
48
+ "ontology": "Q324254", # Ontology
49
+ "organization": "Q95", # Google
50
+ "organizations": "Q95",
51
+ "pathway": "Q26487", # Metabolic pathway
52
+ "podcast": "Q24634210", # The Ozymandias Project
53
+ "printer": "Q11226", # Printer (the device/person)
54
+ "project": "Q5562725", # WikiProject
55
+ "property": "P31", # Instance of
56
+ "protein": "Q11060", # Green fluorescent protein
57
+ "publisher": "Q203019", # Elsevier
58
+ "retraction": "Q45263695", # Retracted paper example
59
+ "series": "Q245209", # Book series
60
+ "software": "Q28865", # Python
61
+ "sponsor": "Q95", # Google
62
+ "taxon": "Q140", # Lion
63
+ "topic": "Q21198", # Computer Science
64
+ "topics": "Q21198",
65
+ "use": "Q1",
66
+ "uses": "Q1",
67
+ "venue": "Q180445", # Nature (Journal)
68
+ "venues": "Q180445",
69
+ "wikiproject": "Q5562725",
70
+ "work": "Q13442814", # Scientific Article
71
+ "works": "Q13442814"
72
+ }
73
+
74
+ def __init__(self, debug: bool = False):
75
+ self.debug = debug
76
+
77
+ def _infer_param_details(self, var_name: str, aspect: Optional[str] = None) -> Dict[str, Any]:
78
+ """
79
+ Heuristics to determine type and default value based on variable name
80
+ and the query aspect (category).
81
+
82
+ Args:
83
+ var_name: The variable name found in the jinja template (e.g. 'q', 'target').
84
+ aspect: The scholia aspect derived from the query name (e.g. 'venue', 'author').
85
+ """
86
+ # Global Default
87
+ details = {"type": "string", "default": "Q80"}
88
+
89
+ # 1. Identify Type based on variable name
90
+ if var_name in ['q', 'target', 'item', 'person', 'work', 'venue', 'gene', 'disease']:
91
+ details["type"] = "WikidataItem"
92
+
93
+ # 2. Assign Default Value based on Aspect
94
+ if aspect and aspect in self.ASPECT_DEFAULTS:
95
+ details["default"] = self.ASPECT_DEFAULTS[aspect]
96
+
97
+ # 3. Fallback defaults if aspect is missing or generic
98
+ elif var_name == 'person':
99
+ details["default"] = "Q80" # Tim Berners-Lee
100
+ elif var_name == 'work':
101
+ details["default"] = "Q13442814"
102
+ else:
103
+ details["default"] = "Q80"
104
+
105
+ elif var_name in ['p', 'property']:
106
+ details["type"] = "WikidataProperty"
107
+ details["default"] = "P31"
108
+ elif var_name in ['limit']:
109
+ details["type"] = "int"
110
+ details["default"] = 10
111
+ elif var_name in ['lang', 'language']:
112
+ details["type"] = "string"
113
+ details["default"] = "en"
114
+
115
+ return details
116
+
117
+ def get_queries_by_aspect(self,query_list:List[Query])->Dict[str,List[Query]]:
118
+ """
119
+ get queries by aspect
120
+ """
121
+ queries_by_aspect = {}
122
+ for query in query_list:
123
+ aspect=self.get_aspect(query.name)
124
+ if aspect in self.ASPECT_DEFAULTS:
125
+ if aspect not in queries_by_aspect:
126
+ queries_by_aspect[aspect] = []
127
+ queries_by_aspect[aspect].append(query)
128
+ return queries_by_aspect
129
+
130
+ def store_query_list(self,query_list:List[Query],tag:str,path:str="/tmp/queries"):
131
+ """
132
+ Store query list sorted by aspect into separate YAML files.
133
+
134
+ Args:
135
+ query_list (List[Query]): List of Query objects with aspect metadata.
136
+ tag (str): Tag to include in filename.
137
+ path (str): Directory path where YAML files will be stored.
138
+ """
139
+ queries_by_aspect=self.get_queries_by_aspect(query_list)
140
+ # Create directory if it doesn't exist
141
+ os.makedirs(path, exist_ok=True)
142
+
143
+ # Store each aspect's queries in a separate YAML file
144
+ for aspect, queries in sorted(queries_by_aspect.items()):
145
+ filename = f"{tag}_{aspect}_queries.yaml"
146
+ filepath = os.path.join(path, filename)
147
+
148
+ with open(filepath, "w", encoding="utf-8") as f:
149
+ for query in queries:
150
+ # work around https://github.com/yaml/pyyaml/issues/411
151
+ if query.sparql:
152
+ clean_sparql = textwrap.dedent(query.sparql).strip()
153
+ yaml_str=query.to_yaml()
154
+ f.write(yaml_str)
155
+ pass
156
+
157
+
158
+ def parameterize_query_set(self, query_set: NamedQuerySet) -> List[Query]:
159
+ """
160
+ Parameterize the given query set to produce a list of LoDStorage queries.
161
+
162
+ Args:
163
+ query_set (NamedQuerySet): The set of named queries to transform.
164
+
165
+ Returns:
166
+ List[Query]: A list of transformed LoDStorage Query objects.
167
+ """
168
+ query_list = []
169
+ for nq in query_set.queries:
170
+ query = self.parameterize_query(nq)
171
+ query_list.append(query)
172
+ return query_list
173
+
174
+ def get_aspect(self,name:str)->str:
175
+ aspect = None
176
+ if name:
177
+ # Split by hyphen first, as that is the standard separator in the example
178
+ parts = re.split(r'[-_]', name)
179
+ if parts:
180
+ aspect = parts[0].lower()
181
+ return aspect
182
+
183
+ def parameterize_query(self, nq: NamedQuery) -> Query:
184
+ """
185
+ Parameterize a single named query.
186
+
187
+ 1. Identifies the "Aspect" from the query name (e.g. 'venue-...' -> 'venue').
188
+ 2. Cleans Jinja2 specific imports.
189
+ 3. Replaces Scholia Macros with standard SPARQL.
190
+ 4. Detects variables and creates Param objects using aspect-specific defaults.
191
+
192
+ Args:
193
+ nq (NamedQuery): The named query input.
194
+
195
+ Returns:
196
+ Query: The transformed LoDStorage Query object.
197
+ """
198
+ # 0. Determine Aspect
199
+ # Naming convention: {aspect}-{subname} or {aspect}_{subname}
200
+ # e.g., "venue-curation_missing-author-item" -> "venue"
201
+ aspect=self.get_aspect(nq.name)
202
+
203
+ raw_sparql = nq.sparql
204
+
205
+ # 1. Remove Jinja imports
206
+ # e.g., {% import 'sparql-helpers.sparql' as sparql_helpers -%}
207
+ txt = re.sub(r'\{%\s*import\s*.*?%\}', '', raw_sparql, flags=re.DOTALL)
208
+
209
+ # 2. Replace Label/Description Macros
210
+ # Matches: {{ sparql_helpers.labels(["?mol"], languages) }}
211
+ txt = re.sub(
212
+ r"\{\{\s*sparql_helpers\.(labels|descriptions).*?\}\}",
213
+ self.LABEL_SERVICE,
214
+ txt,
215
+ flags=re.DOTALL
216
+ )
217
+
218
+ # 3. Detect Variables to build Param list
219
+ found_vars = set(re.findall(r"\{\{\s*([a-zA-Z0-9_]+)\s*\}\}", txt))
220
+ found_vars.discard('sparql_helpers')
221
+
222
+ # Create lodstorage Query object
223
+ clean_sparql = txt.strip()
224
+
225
+ query_obj = Query(
226
+ name=nq.name,
227
+ query=clean_sparql,
228
+ title=nq.title,
229
+ description=nq.description,
230
+ lang='sparql'
231
+ )
232
+
233
+ # 4. Attach Parameters with Aspect context
234
+ param_list = []
235
+ for var in sorted(found_vars):
236
+ details = self._infer_param_details(var, aspect=aspect)
237
+ p = Param(
238
+ name=var,
239
+ type=details["type"],
240
+ default_value=details["default"]
241
+ )
242
+ param_list.append(p)
243
+
244
+ query_obj.param_list = param_list
245
+
246
+ return query_obj
@@ -7,8 +7,8 @@ endpoints:
7
7
  Wikimedia Wikidata Query Service (legacy full).
8
8
  Stable but high-load; prefer -main/-scholarly.
9
9
  lang: sparql
10
- endpoint: https://query.wikidata.org/sparql
11
- website: https://query.wikidata.org
10
+ endpoint: https://query-legacy-full.wikidata.org/sparql
11
+ website: https://query-legacy-full.wikidata.org
12
12
  database: blazegraph
13
13
  method: POST
14
14
  calls_per_minute: 30
@@ -113,6 +113,23 @@ endpoints:
113
113
  - rdf
114
114
  - wikidata
115
115
 
116
+ wikidata-openlink:
117
+ description: |
118
+ OpenLink Wikidata Truthy instance (LOD Cloud demo).
119
+ Optimized for property path queries and transitive closure.
120
+ https://www.openlinksw.com/data/html/about-wikidata-truthy-2025-11.html
121
+ lang: sparql
122
+ endpoint: https://truthy.demo.openlinksw.com/sparql
123
+ website: https://truthy.demo.openlinksw.com
124
+ database: virtuoso
125
+ method: POST
126
+ calls_per_minute: 30
127
+ data_seeded: 2025-11-01
128
+ prefix_sets:
129
+ - rdf
130
+ - wikidata
131
+ - commons
132
+
116
133
  dblp:
117
134
  description: DBLP official (qlever - reliable).
118
135
  lang: sparql
@@ -18,7 +18,7 @@ class Version:
18
18
  name = "snapquery"
19
19
  version = snapquery.__version__
20
20
  date = "2024-05-03"
21
- updated = "2025-12-11"
21
+ updated = "2025-12-17"
22
22
  description = "Introduce Named Queries and Named Query Middleware to wikidata"
23
23
 
24
24
  authors = "Wolfgang Fahl"
@@ -42,9 +42,11 @@ class TestCommandLine(Basetest):
42
42
 
43
43
  # Capture the output of running the command
44
44
  output = self.capture_stdout(run_cmd)
45
- if self.debug:
45
+ debug=self.debug
46
+ #debug=True
47
+ if debug:
46
48
  print(output)
47
- self.assertTrue("wikidata:https://query.wikidata.org" in output)
49
+ self.assertTrue("wikidata:https://query-legacy-full" in output)
48
50
 
49
51
  @unittest.skipIf(Basetest.inPublicCI(), "reading stdout in CI returns None")
50
52
  def test_namedquery(self):
@@ -0,0 +1,123 @@
1
+ """
2
+ Created on 2024-05-04
3
+
4
+ @author: wf
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+ import unittest
10
+ from basemkit.basetest import Basetest
11
+ from snapquery.scholia import ScholiaQueries, GitHubQueries
12
+ from snapquery.snapquery_core import NamedQueryManager, NamedQuerySet
13
+ from snapquery.query_parameterizer import QueryParameterizer
14
+
15
+ class TestScholia(Basetest):
16
+ """
17
+ test scholia queries
18
+ """
19
+
20
+ def setUp(self, debug=True, profile=True):
21
+ Basetest.setUp(self, debug=debug, profile=profile)
22
+ self.samples_dir = Path(__file__).parent.parent / "snapquery" / "samples" / "query-sets" / "yaml"
23
+
24
+ def prepare_nqm(self,prefix:str):
25
+ self.prefix=prefix
26
+ db_path = f"/tmp/{self.prefix}_queries.db"
27
+ if os.path.exists(db_path):
28
+ os.remove(db_path)
29
+ # Adjust limit based on environment (e.g., CI)
30
+ if self.inPublicCI():
31
+ self.limit = 10
32
+ else:
33
+ self.limit = None
34
+ # limit=10
35
+ # Create a NamedQueryManager and ScholiaQueries instance
36
+ self.nqm = NamedQueryManager.from_samples(db_path=db_path)
37
+
38
+ def check_queries(self,queries,):
39
+ queries.store_queries()
40
+ queries.save_to_json(f"/tmp/{self.prefix}.json")
41
+
42
+ # Verify the data was stored
43
+ records = self.nqm.sql_db.query(
44
+ f"""SELECT *
45
+ FROM NamedQuery
46
+ WHERE namespace='{queries.named_query_set.namespace}'
47
+ AND domain='{queries.named_query_set.domain}'
48
+ """
49
+ )
50
+ self.assertEqual(len(records), len(queries.named_query_set.queries))
51
+
52
+
53
+ @unittest.skipIf(Basetest.inPublicCI(), "avoid github rate limit")
54
+ def test_scholia_queries(self):
55
+ """
56
+ Test retrieving Scholia queries.
57
+ """
58
+ prefix="scholia"
59
+ nqm=self.prepare_nqm(prefix)
60
+ scholia_queries = ScholiaQueries(nqm, debug=self.debug)
61
+
62
+ # Extract, store, and save queries to JSON
63
+ scholia_queries.extract_queries(limit=self.limit)
64
+ self.check_queries(scholia_queries)
65
+
66
+ @unittest.skipIf(Basetest.inPublicCI(), "avoid github rate limit")
67
+ def test_scholia_qlever(self):
68
+ """
69
+ Test retrieving Scholia QLever queries from the specific branch.
70
+ """
71
+ prefix = "scholia_qlever"
72
+ nqm = self.prepare_nqm(prefix)
73
+
74
+ # Configure for ad-freiburg/scholia branch:qlever
75
+ scholia_qlever = ScholiaQueries(
76
+ nqm,
77
+ owner="ad-freiburg",
78
+ repo="scholia",
79
+ branch="qlever",
80
+ namespace="named_queries_qlever",
81
+ debug=self.debug
82
+ )
83
+
84
+ scholia_qlever.extract_queries(limit=self.limit)
85
+ self.check_queries(scholia_qlever)
86
+
87
+ def test_parameterizing_queries(self):
88
+ """
89
+ test parameterizing scholia queries
90
+ """
91
+ debug=self.debug
92
+ debug=True
93
+ iso_date = "2025-12-16"
94
+ parameterizer = QueryParameterizer(debug=debug)
95
+ for tag in ["scholia","scholia-qlever"]:
96
+ yaml_file=f"{tag}-{iso_date}.yaml"
97
+ yaml_path = self.samples_dir / yaml_file
98
+ query_set=NamedQuerySet.load_from_yaml_file(yaml_path) # @UndefinedVariable
99
+ query_count=len(query_set.queries)
100
+ if debug:
101
+ print(f"Parameterizing {query_count} {tag} queries")
102
+ pquery_list=parameterizer.parameterize_query_set(query_set)
103
+ parameterizer.store_query_list(pquery_list, tag)
104
+ self.assertEqual(len(pquery_list), query_count)
105
+ if debug:
106
+ for i,query in enumerate(pquery_list):
107
+ print(f"{i}:{query.to_yaml()}")
108
+
109
+ @unittest.skipIf(Basetest.inPublicCI(), "avoid github rate limit")
110
+ def test_exploratory_querying_sessions(self):
111
+ owner = "hartig"
112
+ repo= "ExploratoryQueryingSessions"
113
+ path= "/sessions"
114
+ extension = ".rq"
115
+ limit = 5
116
+ prefix="hartig_eqs"
117
+ self.prepare_nqm(prefix)
118
+ show_progress=self.debug
119
+ show_progress=True
120
+ hartig_queries = GitHubQueries(self.nqm, owner=owner,repo=repo, path=path,extension=extension, debug=self.debug)
121
+ hartig_queries.extract_queries(limit=limit, show_progress=show_progress)
122
+ self.check_queries(hartig_queries)
123
+
@@ -1 +0,0 @@
1
- __version__ = "0.2.4"
@@ -1,246 +0,0 @@
1
- """
2
- Created on 2024-05-04
3
-
4
- @author: wf
5
- """
6
- import re
7
- import os
8
- from typing import Dict,List, Any
9
- from pathlib import Path
10
- import unittest
11
- from lodstorage.params import Param
12
- from basemkit.basetest import Basetest
13
- from lodstorage.query import Query
14
- from snapquery.scholia import ScholiaQueries, GitHubQueries
15
- from snapquery.snapquery_core import NamedQueryManager, NamedQuerySet, \
16
- NamedQuery
17
-
18
-
19
- class QueryParameterizer:
20
- """
21
- Parameterizer for Scholia Query Sets.
22
- Transforms Jinja2-based Scholia templates into standard
23
- LoDStorage Parameterized Queries.
24
- """
25
- def __init__(self,debug:bool=False):
26
- self.debug=debug
27
-
28
- # Replacement for Scholia's label macro
29
- LABEL_SERVICE = 'SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }'
30
-
31
- def _infer_param_details(self, var_name: str) -> Dict[str, Any]:
32
- """
33
- Heuristics to determine type and default value based on variable name.
34
- """
35
- details = {"type": "string", "default": "Q80"}
36
-
37
- if var_name in ['q', 'target', 'item', 'person', 'work']:
38
- details["type"] = "WikidataItem"
39
- details["default"] = "Q80" # Tim Berners-Lee
40
- elif var_name in ['p', 'property']:
41
- details["type"] = "WikidataProperty"
42
- details["default"] = "P31"
43
- elif var_name in ['limit']:
44
- details["type"] = "int"
45
- details["default"] = 10
46
- elif var_name in ['lang', 'language']:
47
- details["type"] = "string"
48
- details["default"] = "en"
49
-
50
- return details
51
-
52
- def parameterize_query_set(self, query_set: NamedQuerySet) -> List[Query]:
53
- """
54
- Parameterize the given query set to produce a list of LoDStorage queries.
55
-
56
- Args:
57
- query_set (NamedQuerySet): The set of named queries to transform.
58
-
59
- Returns:
60
- List[Query]: A list of transformed LoDStorage Query objects.
61
- """
62
- query_list = []
63
- for nq in query_set.queries:
64
- query = self.parameterize_query(nq)
65
- query_list.append(query)
66
- return query_list
67
-
68
- def parameterize_query(self, nq: NamedQuery) -> Query:
69
- """
70
- Parameterize a single named query.
71
-
72
- 1. Cleans Jinja2 specific imports.
73
- 2. Replaces Scholia Macros (sparql_helpers) with standard SPARQL.
74
- 3. Detects variables and creates Param objects.
75
-
76
- Args:
77
- nq (NamedQuery): The named query input.
78
-
79
- Returns:
80
- Query: The transformed LoDStorage Query object.
81
- """
82
- raw_sparql = nq.sparql
83
-
84
- # 1. Remove Jinja imports
85
- # e.g., {% import 'sparql-helpers.sparql' as sparql_helpers -%}
86
- txt = re.sub(r'\{%\s*import\s*.*?%\}', '', raw_sparql, flags=re.DOTALL)
87
-
88
- # 2. Replace Label/Description Macros
89
- # Matches: {{ sparql_helpers.labels(["?mol"], languages) }}
90
- txt = re.sub(
91
- r"\{\{\s*sparql_helpers\.(labels|descriptions).*?\}\}",
92
- self.LABEL_SERVICE,
93
- txt,
94
- flags=re.DOTALL
95
- )
96
-
97
- # 3. Detect Variables to build Param list
98
- # Look for {{ var }} patterns.
99
- # Note: We keep the {{ }} syntax or convert to { } depending on how
100
- # the storage engine handles format(), but here we focus on extraction.
101
- found_vars = set(re.findall(r"\{\{\s*([a-zA-Z0-9_]+)\s*\}\}", txt))
102
- found_vars.discard('sparql_helpers') # Ensure module names aren't params
103
-
104
- # Create lodstorage Query object
105
- # We strip surrounding whitespace generated by Jinja removals
106
- clean_sparql = txt.strip()
107
-
108
- query_obj = Query(
109
- name=nq.name,
110
- query=clean_sparql,
111
- title=nq.title,
112
- description=nq.description,
113
- lang='sparql'
114
- )
115
-
116
- # 4. Attach Parameters
117
- # Currently lodstorage Query doesn't have a direct 'params' list in __init__,
118
- # usually it's handled via a dict or external management.
119
- # However, we can use the `params` attribute if we wish to store metadata.
120
- # We will create a list of Param objects.
121
-
122
- param_list = []
123
- for var in sorted(found_vars):
124
- details = self._infer_param_details(var)
125
- p = Param(
126
- name=var,
127
- type=details["type"],
128
- default_value=details["default"]
129
- )
130
- param_list.append(p)
131
-
132
- # Store params in the query object for subsequent usage/testing
133
- # (This assumes the Query object is dynamic or we tack it on)
134
- query_obj.param_list = param_list
135
-
136
- return query_obj
137
-
138
-
139
- class TestScholia(Basetest):
140
- """
141
- test scholia queries
142
- """
143
-
144
- def setUp(self, debug=True, profile=True):
145
- Basetest.setUp(self, debug=debug, profile=profile)
146
- self.samples_dir = Path(__file__).parent.parent / "snapquery" / "samples" / "query-sets" / "yaml"
147
-
148
- def prepare_nqm(self,prefix:str):
149
- self.prefix=prefix
150
- db_path = f"/tmp/{self.prefix}_queries.db"
151
- if os.path.exists(db_path):
152
- os.remove(db_path)
153
- # Adjust limit based on environment (e.g., CI)
154
- if self.inPublicCI():
155
- self.limit = 10
156
- else:
157
- self.limit = None
158
- # limit=10
159
- # Create a NamedQueryManager and ScholiaQueries instance
160
- self.nqm = NamedQueryManager.from_samples(db_path=db_path)
161
-
162
- def check_queries(self,queries,):
163
- queries.store_queries()
164
- queries.save_to_json(f"/tmp/{self.prefix}.json")
165
-
166
- # Verify the data was stored
167
- records = self.nqm.sql_db.query(
168
- f"""SELECT *
169
- FROM NamedQuery
170
- WHERE namespace='{queries.named_query_set.namespace}'
171
- AND domain='{queries.named_query_set.domain}'
172
- """
173
- )
174
- self.assertEqual(len(records), len(queries.named_query_set.queries))
175
-
176
-
177
- @unittest.skipIf(Basetest.inPublicCI(), "avoid github rate limit")
178
- def test_scholia_queries(self):
179
- """
180
- Test retrieving Scholia queries.
181
- """
182
- prefix="scholia"
183
- nqm=self.prepare_nqm(prefix)
184
- scholia_queries = ScholiaQueries(nqm, debug=self.debug)
185
-
186
- # Extract, store, and save queries to JSON
187
- scholia_queries.extract_queries(limit=self.limit)
188
- self.check_queries(scholia_queries)
189
-
190
- @unittest.skipIf(Basetest.inPublicCI(), "avoid github rate limit")
191
- def test_scholia_qlever(self):
192
- """
193
- Test retrieving Scholia QLever queries from the specific branch.
194
- """
195
- prefix = "scholia_qlever"
196
- nqm = self.prepare_nqm(prefix)
197
-
198
- # Configure for ad-freiburg/scholia branch:qlever
199
- scholia_qlever = ScholiaQueries(
200
- nqm,
201
- owner="ad-freiburg",
202
- repo="scholia",
203
- branch="qlever",
204
- namespace="named_queries_qlever",
205
- debug=self.debug
206
- )
207
-
208
- scholia_qlever.extract_queries(limit=self.limit)
209
- self.check_queries(scholia_qlever)
210
-
211
- def test_parameterizing_queries(self):
212
- """
213
- test parameterizing scholia queries
214
- """
215
- debug=self.debug
216
- debug=True
217
- iso_date = "2025-12-16"
218
- parameterizer = QueryParameterizer(debug=debug)
219
- for tag in ["scholia","scholia-qlever"]:
220
- yaml_file=f"{tag}-{iso_date}.yaml"
221
- yaml_path = self.samples_dir / yaml_file
222
- query_set=NamedQuerySet.load_from_yaml_file(yaml_path) # @UndefinedVariable
223
- query_count=len(query_set.queries)
224
- if debug:
225
- print(f"Parameterizing {query_count} {tag} queries")
226
- pquery_list=parameterizer.parameterize_query_set(query_set)
227
- self.assertEqual(len(pquery_list), query_count)
228
- if debug:
229
- for i,query in enumerate(pquery_list):
230
- print(f"{i}:{query.to_yaml()}")
231
-
232
- @unittest.skipIf(Basetest.inPublicCI(), "avoid github rate limit")
233
- def test_exploratory_querying_sessions(self):
234
- owner = "hartig"
235
- repo= "ExploratoryQueryingSessions"
236
- path= "/sessions"
237
- extension = ".rq"
238
- limit = 5
239
- prefix="hartig_eqs"
240
- self.prepare_nqm(prefix)
241
- show_progress=self.debug
242
- show_progress=True
243
- hartig_queries = GitHubQueries(self.nqm, owner=owner,repo=repo, path=path,extension=extension, debug=self.debug)
244
- hartig_queries.extract_queries(limit=limit, show_progress=show_progress)
245
- self.check_queries(hartig_queries)
246
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes