snapquery 0.2.3__tar.gz → 0.2.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.
Files changed (143) hide show
  1. {snapquery-0.2.3 → snapquery-0.2.4}/PKG-INFO +5 -5
  2. {snapquery-0.2.3 → snapquery-0.2.4}/pyproject.toml +4 -4
  3. snapquery-0.2.4/snapquery/__init__.py +1 -0
  4. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/github_access.py +25 -10
  5. snapquery-0.2.4/snapquery/qlever.py +187 -0
  6. snapquery-0.2.4/snapquery/query_set_cmd.py +435 -0
  7. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/query_set_tool.py +11 -15
  8. snapquery-0.2.4/snapquery/samples/query-sets/yaml/hartig-eqs-2025-12-16.yaml +3033 -0
  9. snapquery-0.2.4/snapquery/samples/query-sets/yaml/qlever-issues-2025-12-05.yaml +6219 -0
  10. snapquery-0.2.4/snapquery/samples/query-sets/yaml/scholia-2025-12-04.yaml +9324 -0
  11. snapquery-0.2.4/snapquery/samples/query-sets/yaml/scholia-2025-12-16.yaml +9324 -0
  12. snapquery-0.2.4/snapquery/samples/query-sets/yaml/scholia-qlever-2025-12-16.yaml +14907 -0
  13. snapquery-0.2.4/snapquery/samples/query-sets/yaml/sib-2025-12-04.yaml +24822 -0
  14. snapquery-0.2.4/snapquery/samples/query-sets/yaml/wikidata-examples-2025-12-04.yaml +8715 -0
  15. snapquery-0.2.4/snapquery/samples/query-sets/yaml/wikidata-short-urls-2025-12-06.yaml +1541 -0
  16. snapquery-0.2.4/snapquery/scholia.py +161 -0
  17. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/sib_sparql_examples.py +13 -7
  18. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/snapquery_cmd.py +11 -3
  19. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/snapquery_webserver.py +8 -2
  20. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/sparql_analyzer.py +1 -0
  21. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/version.py +1 -1
  22. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/wd_page_query_extractor.py +41 -15
  23. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/wd_short_url.py +13 -10
  24. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_endpoints.py +3 -3
  25. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_qlever.py +18 -36
  26. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_query_set_tool.py +4 -3
  27. snapquery-0.2.4/tests/test_scholia.py +246 -0
  28. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_sib_examples.py +10 -6
  29. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_sparql_analyzer.py +1 -1
  30. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_wd_query_parsing.py +3 -3
  31. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_wd_short_url.py +4 -1
  32. snapquery-0.2.3/snapquery/__init__.py +0 -1
  33. snapquery-0.2.3/snapquery/qlever.py +0 -111
  34. snapquery-0.2.3/snapquery/query_set_cmd.py +0 -168
  35. snapquery-0.2.3/snapquery/scholia.py +0 -107
  36. snapquery-0.2.3/tests/test_scholia.py +0 -57
  37. {snapquery-0.2.3 → snapquery-0.2.4}/.github/workflows/build.yml +0 -0
  38. {snapquery-0.2.3 → snapquery-0.2.4}/.github/workflows/upload-to-pypi.yml +0 -0
  39. {snapquery-0.2.3 → snapquery-0.2.4}/.gitignore +0 -0
  40. {snapquery-0.2.3 → snapquery-0.2.4}/.project +0 -0
  41. {snapquery-0.2.3 → snapquery-0.2.4}/.pydevproject +0 -0
  42. {snapquery-0.2.3 → snapquery-0.2.4}/CITATION.cff +0 -0
  43. {snapquery-0.2.3 → snapquery-0.2.4}/LICENSE +0 -0
  44. {snapquery-0.2.3 → snapquery-0.2.4}/README.md +0 -0
  45. {snapquery-0.2.3 → snapquery-0.2.4}/docs/index.md +0 -0
  46. {snapquery-0.2.3 → snapquery-0.2.4}/mkdocs.yml +0 -0
  47. {snapquery-0.2.3 → snapquery-0.2.4}/scripts/blackisort +0 -0
  48. {snapquery-0.2.3 → snapquery-0.2.4}/scripts/doc +0 -0
  49. {snapquery-0.2.3 → snapquery-0.2.4}/scripts/fixdomain +0 -0
  50. {snapquery-0.2.3 → snapquery-0.2.4}/scripts/install +0 -0
  51. {snapquery-0.2.3 → snapquery-0.2.4}/scripts/qlv +0 -0
  52. {snapquery-0.2.3 → snapquery-0.2.4}/scripts/release +0 -0
  53. {snapquery-0.2.3 → snapquery-0.2.4}/scripts/restore_db +0 -0
  54. {snapquery-0.2.3 → snapquery-0.2.4}/scripts/test +0 -0
  55. {snapquery-0.2.3 → snapquery-0.2.4}/scripts/test_queries +0 -0
  56. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/authorization.py +0 -0
  57. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/basequeryview.py +0 -0
  58. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/ceurws.py +0 -0
  59. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/dblp.py +0 -0
  60. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/error_filter.py +0 -0
  61. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/execution.py +0 -0
  62. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/graph.py +0 -0
  63. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/models/__init__.py +0 -0
  64. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/models/person.py +0 -0
  65. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/models/sparql_components.py +0 -0
  66. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/mwlogin.py +0 -0
  67. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/namespace_stats_view.py +0 -0
  68. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/orcid.py +0 -0
  69. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/params_view.py +0 -0
  70. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/person_selector.py +0 -0
  71. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/pid.py +0 -0
  72. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/pid_lookup.py +0 -0
  73. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/prefix_merger.py +0 -0
  74. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/query_annotate.py +0 -0
  75. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/query_selector.py +0 -0
  76. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/query_set_tool_view.py +0 -0
  77. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/resources/sparql_language.json +0 -0
  78. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/WikiProject_LSEThesisProject.wiki +0 -0
  79. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/ceur-ws.json +0 -0
  80. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/dblp.json +0 -0
  81. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/endpoints.yaml +0 -0
  82. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/endpoints_optional.yaml +0 -0
  83. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/federated-query-examples.json +0 -0
  84. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/graphs.yaml +0 -0
  85. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/meta_query.yaml +0 -0
  86. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/orkg.json +0 -0
  87. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/qlever.json +0 -0
  88. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/qlever_performance.json +0 -0
  89. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/ceur-ws.yaml +0 -0
  90. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/dblp.yaml +0 -0
  91. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/federated-query-examples.yaml +0 -0
  92. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/orkg.yaml +0 -0
  93. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/qlever.yaml +0 -0
  94. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/qlever_performance.yaml +0 -0
  95. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/scholia.yaml +0 -0
  96. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/snapquery.yaml +0 -0
  97. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/wikidata-WikidataThesisToolkit.yaml +0 -0
  98. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/wikidata-WikidataThesisToolkitAlt.yaml +0 -0
  99. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/wikidata-examples.yaml +0 -0
  100. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query-sets/yaml/wikidata-short-urls.yaml +0 -0
  101. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/query_stats.yaml +0 -0
  102. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/scholia.json +0 -0
  103. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/snapquery.json +0 -0
  104. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/wikidata-WikidataThesisToolkit.json +0 -0
  105. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/wikidata-WikidataThesisToolkitAlt.json +0 -0
  106. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/wikidata-examples.json +0 -0
  107. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/samples/wikidata-short-urls.json +0 -0
  108. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/snapquery_core.py +0 -0
  109. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/snapquery_view.py +0 -0
  110. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery/stats_view.py +0 -0
  111. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery.puml +0 -0
  112. {snapquery-0.2.3 → snapquery-0.2.4}/snapquery.yaml +0 -0
  113. {snapquery-0.2.3 → snapquery-0.2.4}/tests/__init__.py +0 -0
  114. {snapquery-0.2.3 → snapquery-0.2.4}/tests/action_stats.py +0 -0
  115. {snapquery-0.2.3 → snapquery-0.2.4}/tests/query_extractors/__init__.py +0 -0
  116. {snapquery-0.2.3 → snapquery-0.2.4}/tests/query_extractors/sparql_standard_queries.py +0 -0
  117. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/qlever/ask_unsupported.txt +0 -0
  118. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/qlever/invalid_literal.txt +0 -0
  119. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/qlever/invalid_regex_func.txt +0 -0
  120. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/qlever/memory_limit.txt +0 -0
  121. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/qlever/timeout.txt +0 -0
  122. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/triplydb/parser_error.txt +0 -0
  123. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/wdqs/duplicate_prefix.txt +0 -0
  124. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/wdqs/invalid_iri_declaration.txt +0 -0
  125. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/wdqs/invalid_sparql_syntax.txt +0 -0
  126. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/wdqs/timeout.txt +0 -0
  127. {snapquery-0.2.3 → snapquery-0.2.4}/tests/resources/error_messages/wdqs/undefined_prefix.txt +0 -0
  128. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_authorization.py +0 -0
  129. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_ceurws.py +0 -0
  130. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_cmdline.py +0 -0
  131. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_error_filter.py +0 -0
  132. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_graph.py +0 -0
  133. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_namedqueries.py +0 -0
  134. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_oauth.py +0 -0
  135. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_orcid.py +0 -0
  136. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_pids.py +0 -0
  137. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_prefix_merger.py +0 -0
  138. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_query_annotate.py +0 -0
  139. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_query_execution.py +0 -0
  140. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_query_name.py +0 -0
  141. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_restful_api.py +0 -0
  142. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_scholia_q.py +0 -0
  143. {snapquery-0.2.3 → snapquery-0.2.4}/tests/test_sparql_components.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snapquery
3
- Version: 0.2.3
3
+ Version: 0.2.4
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
@@ -26,14 +26,14 @@ Classifier: Topic :: Software Development :: User Interfaces
26
26
  Requires-Python: >=3.10
27
27
  Requires-Dist: mwoauth>=0.4.0
28
28
  Requires-Dist: nameparser>=1.1.3
29
- Requires-Dist: ngwidgets>=0.30.0
29
+ Requires-Dist: ngwidgets>=0.30.3
30
30
  Requires-Dist: nicegui[plotly]==2.24.2
31
31
  Requires-Dist: pandas>=2.3.3
32
32
  Requires-Dist: py-3rdparty-mediawiki>=0.18.1
33
33
  Requires-Dist: py-ez-wikidata>=0.3.2
34
- Requires-Dist: pybasemkit>=0.1.3
35
- Requires-Dist: pylodstorage>=0.18.5
36
- Requires-Dist: pyopensourceprojects>=0.3.0
34
+ Requires-Dist: pybasemkit>=0.1.6
35
+ Requires-Dist: pylodstorage>=0.18.6
36
+ Requires-Dist: pyopensourceprojects>=0.4.1
37
37
  Requires-Dist: python-slugify>=8.0.4
38
38
  Requires-Dist: ratelimit>=2.2.1
39
39
  Requires-Dist: requests
@@ -22,11 +22,11 @@ dependencies = [
22
22
  # https://pypi.org/project/pybasemkit/
23
23
  # https://github.com/WolfgangFahl/pybasemkit
24
24
  # Python base module kit: YAML/JSON I/O, structured logging, CLI tooling, shell execution, and remote pydevd debug support.
25
- "pybasemkit>=0.1.3",
25
+ "pybasemkit>=0.1.6",
26
26
  # https://github.com/WolfgangFahl/nicegui_widgets
27
- "ngwidgets>=0.30.0",
27
+ "ngwidgets>=0.30.3",
28
28
  # https://pypi.org/project/pyLodStorage/
29
- "pyLodStorage>=0.18.5",
29
+ "pyLodStorage>=0.18.6",
30
30
  # https://pypi.org/project/py-3rdparty-mediawiki/
31
31
  "py-3rdparty-mediawiki>=0.18.1",
32
32
  "wikitextparser",
@@ -51,7 +51,7 @@ dependencies = [
51
51
  # https://pypi.org/project/nameparser/
52
52
  "nameparser>=1.1.3",
53
53
  # https://github.com/WolfgangFahl/pyOpenSourceProjects
54
- "pyOpenSourceProjects>=0.3.0"
54
+ "pyOpenSourceProjects>=0.4.1"
55
55
  ]
56
56
 
57
57
  requires-python = ">=3.10"
@@ -0,0 +1 @@
1
+ __version__ = "0.2.4"
@@ -4,9 +4,10 @@ GitHub API client for accessing repository contents.
4
4
  Created on 2025-12-02
5
5
  @author: wf
6
6
  """
7
+
7
8
  import json
8
- from pathlib import Path
9
- from typing import List, Dict, Any, Optional, Union
9
+ from pathlib import Path
10
+ from typing import Any, Dict, List, Optional, Union
10
11
 
11
12
  import requests
12
13
 
@@ -16,28 +17,38 @@ class GitHub:
16
17
  A simple GitHub API client for accessing repository contents.
17
18
  """
18
19
 
19
- def __init__(self, owner: str, repo: str, token: Optional[str] = None, session: Optional[requests.Session] = None):
20
+ def __init__(
21
+ self,
22
+ owner: str,
23
+ repo: str,
24
+ branch: Optional[str] = None,
25
+ token: Optional[str] = None,
26
+ session: Optional[requests.Session] = None
27
+ ):
20
28
  """
21
29
  Initialize GitHub client.
22
30
 
23
31
  Args:
24
32
  owner: Repository owner (username or organization)
25
33
  repo: Repository name
34
+ branch: Optional specific branch or commit SHA (default: default repo branch)
26
35
  token: Optional GitHub API token for authentication
27
36
  session: Optional custom requests.Session
28
37
  """
29
38
  self.owner = owner
30
39
  self.repo = repo
31
- self.token = token
40
+ self.branch = branch
41
+
42
+
32
43
  self.base_url = f"https://api.github.com/repos/{owner}/{repo}"
33
- # Use provided token or read from file (compatible with GitHubApi)
44
+
45
+ # Use provided token or read from file
34
46
  self.token = token if token is not None else self._read_token()
35
47
 
36
48
  # Use custom session or create new one
37
49
  self.session = session or requests.Session()
38
50
 
39
51
 
40
-
41
52
  def _headers(self) -> Dict[str, str]:
42
53
  headers = {"Accept": "application/vnd.github.v3+json"}
43
54
  if self.token:
@@ -55,14 +66,13 @@ class GitHub:
55
66
  token_path = Path.home() / ".github" / "access_token.json"
56
67
  if token_path.exists():
57
68
  try:
58
- with open(token_path, 'r') as f:
69
+ with open(token_path, "r") as f:
59
70
  data = json.load(f)
60
71
  return data.get("access_token")
61
72
  except (json.JSONDecodeError, OSError):
62
73
  pass
63
74
  return None
64
75
 
65
-
66
76
  def get_contents(self, path: str = "") -> Union[List[Dict[str, Any]], Dict[str, Any]]:
67
77
  """
68
78
  Get contents of a directory or file from the repository.
@@ -74,10 +84,15 @@ class GitHub:
74
84
  List of dictionaries for directories, or content for files
75
85
  """
76
86
  url = f"{self.base_url}/contents/{path}"
77
- response = self.session.get(url, headers=self._headers(), timeout=30)
87
+ params = {}
88
+ if self.branch:
89
+ params["ref"] = self.branch
90
+
91
+ response = self.session.get(url, headers=self._headers(), params=params, timeout=30)
78
92
  response.raise_for_status()
79
93
  return response.json()
80
94
 
95
+
81
96
  def list_files_recursive(self, path: str = "", suffix: Optional[str] = None) -> List[Dict[str, Any]]:
82
97
  """
83
98
  Recursively list files under a given path. Optionally filter by file suffix.
@@ -116,4 +131,4 @@ class GitHub:
116
131
  """
117
132
  response = self.session.get(download_url, headers=self._headers(), timeout=30)
118
133
  response.raise_for_status()
119
- return response.text
134
+ return response.text
@@ -0,0 +1,187 @@
1
+ """
2
+ Created on 2024-06-20
3
+
4
+ @author: wf
5
+ """
6
+
7
+ import re
8
+ from typing import Dict, List
9
+
10
+ from bs4 import BeautifulSoup
11
+ from osprojects.osproject import OsProject, Ticket
12
+ from tqdm import tqdm
13
+
14
+ from snapquery.snapquery_core import NamedQuery, NamedQuerySet
15
+ from snapquery.wd_short_url import ShortUrl
16
+
17
+
18
+ class QLeverUrl(ShortUrl):
19
+ """
20
+ Handles operations related to QLever short URLs.
21
+ """
22
+
23
+ def __init__(self, short_url: str):
24
+ super().__init__(short_url, scheme="https", netloc="qlever.cs.uni-freiburg.de")
25
+ self.sparql = None
26
+
27
+ def read_query(self) -> str:
28
+ """
29
+ Read a query from a QLever short URL.
30
+ It follows redirects and parses the HTML to find the query in the textarea.
31
+
32
+ Returns:
33
+ str: The SPARQL query extracted from the short URL.
34
+ """
35
+ # Ensure we have the final URL (handles redirects)
36
+ self.fetch_final_url(self.short_url)
37
+
38
+ if self.url:
39
+ try:
40
+ # Assuming ShortUrl or its parent has a get_content or similar method.
41
+ # If not, specific request logic would go here.
42
+ # Using get_wikitext as per your snippet pattern, assuming it returns page content.
43
+ text = self.get_wikitext(self.url)
44
+ if text:
45
+ soup = BeautifulSoup(text, "html.parser")
46
+ query_element = soup.find("textarea", {"id": "query"})
47
+ if query_element and query_element.text:
48
+ self.sparql = query_element.text.strip()
49
+ except Exception as ex:
50
+ self.error = ex
51
+ return self.sparql
52
+
53
+
54
+ class QLever:
55
+ """
56
+ Handle https://github.com/ad-freiburg/qlever specifics
57
+ and interaction with QLever instances.
58
+ """
59
+
60
+ def __init__(
61
+ self,
62
+ with_progress: bool = True,
63
+ debug: bool = False,
64
+ ):
65
+ """
66
+ constructor
67
+ """
68
+ self.url = "https://github.com/ad-freiburg/qlever"
69
+ self.with_progress = with_progress
70
+ self.debug = debug
71
+ # Regex pattern to find URLs starting with the specified prefix
72
+ self.wd_url_pattern = re.compile(r"https://(?:qlever\.cs\.uni-freiburg\.de|qlever\.dev)/wikidata/[A-Za-z0-9]+")
73
+ self.osproject = OsProject.fromUrl(self.url)
74
+
75
+ def wd_urls_for_ticket(self, ticket: Ticket) -> List[str]:
76
+ """
77
+ Extracts and returns all URLs from a ticket's body and comments
78
+ that match the QLever Wikidata pattern.
79
+ """
80
+ extracted_urls = []
81
+
82
+ # Extract URLs from the ticket body
83
+ if ticket.body:
84
+ found_urls = self.wd_url_pattern.findall(ticket.body)
85
+ extracted_urls.extend(found_urls)
86
+
87
+ # Fetch and extract URLs from comments
88
+ comments = self.osproject.getComments(ticket.number)
89
+ for comment in comments:
90
+ if "body" in comment:
91
+ found_urls = self.wd_url_pattern.findall(comment["body"])
92
+ extracted_urls.extend(found_urls)
93
+
94
+ # distinct list
95
+ ticket_urls = list(set(extracted_urls))
96
+ return ticket_urls
97
+
98
+ def get_issues_query_set(self, limit: int = None, progress: bool = False) -> NamedQuerySet:
99
+ """
100
+ Orchestrates the retrieval of tickets, extraction of URLs,
101
+ resolving of SPARQL queries, and creation of a NamedQuerySet.
102
+
103
+ Args:
104
+ limit (int): Max number of tickets to process.
105
+ progress (bool): Whether to show a CLI progress bar.
106
+
107
+ Returns:
108
+ NamedQuerySet: The populated query set.
109
+ """
110
+ # Get the full dictionary
111
+ tickets_map = self.osproject.getAllTickets(limit=limit)
112
+ items_iterable = tickets_map.items()
113
+
114
+ if progress:
115
+ iterator = tqdm(
116
+ items_iterable, total=len(tickets_map), desc="Scanning tickets for QLever URLs", unit="ticket"
117
+ )
118
+ else:
119
+ iterator = items_iterable
120
+
121
+ ticket_dict = {}
122
+
123
+ # 1. Scan tickets for URLs
124
+ for i, (t_id, ticket) in enumerate(iterator):
125
+ msg = f"fetching short urls for ticket {i}:#{t_id} {ticket.title}"
126
+ if self.debug:
127
+ print(msg)
128
+ urls = self.wd_urls_for_ticket(ticket)
129
+ if urls:
130
+ ticket_dict[ticket] = urls
131
+
132
+ # 2. Convert to NamedQuerySet
133
+ # (This resolves the short URLs to actual SPARQL)
134
+ nq_set = self.named_queries_for_tickets(ticket_dict)
135
+ return nq_set
136
+
137
+ def named_queries_for_tickets(self, ticket_dict: Dict[Ticket, List[str]]) -> NamedQuerySet:
138
+ """
139
+ Create named queries for each ticket's extracted URLs.
140
+
141
+ Args:
142
+ ticket_dict (dict): Dictionary mapping tickets to a list of URLs.
143
+
144
+ Returns:
145
+ NamedQuerySet: A set of named queries generated from the URLs.
146
+ """
147
+ named_query_set = NamedQuerySet(
148
+ domain="qlever.dev",
149
+ namespace="issues.wikidata",
150
+ target_graph_name="wikidata",
151
+ )
152
+
153
+ total_urls = sum(len(urls) for urls in ticket_dict.values())
154
+
155
+ # We might want progress here if there are many URLs to resolve
156
+ if self.with_progress and total_urls > 0:
157
+ pbar = tqdm(total=total_urls, desc="Resolving QLever URLs", unit="url")
158
+ else:
159
+ pbar = None
160
+
161
+ for ticket, urls in ticket_dict.items():
162
+ for i, url in enumerate(urls, 1):
163
+ # Resolve the Short URL
164
+ qlever_url = QLeverUrl(url)
165
+ sparql = qlever_url.read_query()
166
+
167
+ if sparql:
168
+ # Construct the NamedQuery
169
+ query = NamedQuery(
170
+ domain=named_query_set.domain,
171
+ name=f"Issue{ticket.number}_query{i}",
172
+ namespace=named_query_set.namespace,
173
+ url=url,
174
+ sparql=sparql,
175
+ title=f"QLever Issue #{ticket.number} Query {i}",
176
+ description=ticket.title,
177
+ comment=f"Source: Ticket {ticket.url}, QLever Link: {url}",
178
+ )
179
+ named_query_set.queries.append(query)
180
+
181
+ if pbar:
182
+ pbar.update(1)
183
+
184
+ if pbar:
185
+ pbar.close()
186
+
187
+ return named_query_set