qlever 0.2.5__py3-none-any.whl → 0.5.41__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qlever/Qleverfiles/Qleverfile.dblp +36 -0
- qlever/Qleverfiles/Qleverfile.dblp-plus +33 -0
- qlever/Qleverfiles/Qleverfile.dbpedia +30 -0
- qlever/Qleverfiles/Qleverfile.default +51 -0
- qlever/Qleverfiles/Qleverfile.dnb +40 -0
- qlever/Qleverfiles/Qleverfile.fbeasy +29 -0
- qlever/Qleverfiles/Qleverfile.freebase +28 -0
- qlever/Qleverfiles/Qleverfile.imdb +36 -0
- qlever/Qleverfiles/Qleverfile.ohm-planet +41 -0
- qlever/Qleverfiles/Qleverfile.olympics +31 -0
- qlever/Qleverfiles/Qleverfile.orkg +30 -0
- qlever/Qleverfiles/Qleverfile.osm-country +39 -0
- qlever/Qleverfiles/Qleverfile.osm-planet +39 -0
- qlever/Qleverfiles/Qleverfile.osm-planet-from-pbf +42 -0
- qlever/Qleverfiles/Qleverfile.pubchem +131 -0
- qlever/Qleverfiles/Qleverfile.scientists +29 -0
- qlever/Qleverfiles/Qleverfile.uniprot +74 -0
- qlever/Qleverfiles/Qleverfile.vvz +31 -0
- qlever/Qleverfiles/Qleverfile.wikidata +42 -0
- qlever/Qleverfiles/Qleverfile.wikipathways +40 -0
- qlever/Qleverfiles/Qleverfile.yago-4 +33 -0
- qlever/__init__.py +44 -1380
- qlever/command.py +87 -0
- qlever/commands/__init__.py +0 -0
- qlever/commands/add_text_index.py +115 -0
- qlever/commands/benchmark_queries.py +1019 -0
- qlever/commands/cache_stats.py +125 -0
- qlever/commands/clear_cache.py +88 -0
- qlever/commands/extract_queries.py +120 -0
- qlever/commands/get_data.py +48 -0
- qlever/commands/index.py +333 -0
- qlever/commands/index_stats.py +306 -0
- qlever/commands/log.py +66 -0
- qlever/commands/materialized_view.py +110 -0
- qlever/commands/query.py +142 -0
- qlever/commands/rebuild_index.py +176 -0
- qlever/commands/reset_updates.py +59 -0
- qlever/commands/settings.py +115 -0
- qlever/commands/setup_config.py +97 -0
- qlever/commands/start.py +336 -0
- qlever/commands/status.py +50 -0
- qlever/commands/stop.py +90 -0
- qlever/commands/system_info.py +130 -0
- qlever/commands/ui.py +271 -0
- qlever/commands/update.py +90 -0
- qlever/commands/update_wikidata.py +1204 -0
- qlever/commands/warmup.py +41 -0
- qlever/config.py +223 -0
- qlever/containerize.py +167 -0
- qlever/log.py +55 -0
- qlever/qlever_main.py +79 -0
- qlever/qleverfile.py +530 -0
- qlever/util.py +330 -0
- qlever-0.5.41.dist-info/METADATA +127 -0
- qlever-0.5.41.dist-info/RECORD +59 -0
- {qlever-0.2.5.dist-info → qlever-0.5.41.dist-info}/WHEEL +1 -1
- qlever-0.5.41.dist-info/entry_points.txt +2 -0
- qlever-0.5.41.dist-info/top_level.txt +1 -0
- build/lib/qlever/__init__.py +0 -1383
- build/lib/qlever/__main__.py +0 -4
- qlever/__main__.py +0 -4
- qlever-0.2.5.dist-info/METADATA +0 -277
- qlever-0.2.5.dist-info/RECORD +0 -12
- qlever-0.2.5.dist-info/entry_points.txt +0 -2
- qlever-0.2.5.dist-info/top_level.txt +0 -4
- src/qlever/__init__.py +0 -1383
- src/qlever/__main__.py +0 -4
- {qlever-0.2.5.dist-info → qlever-0.5.41.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import platform
|
|
4
|
+
from importlib.metadata import version
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import psutil
|
|
8
|
+
|
|
9
|
+
from qlever.command import QleverCommand
|
|
10
|
+
from qlever.containerize import Containerize
|
|
11
|
+
from qlever.log import log
|
|
12
|
+
from qlever.util import format_size, run_command
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def show_heading(text: str) -> str:
|
|
16
|
+
log.info(text)
|
|
17
|
+
log.info("-" * len(text))
|
|
18
|
+
log.info("")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_partition(dir: Path):
|
|
22
|
+
"""
|
|
23
|
+
Returns the partition on which `dir` resides. May return None.
|
|
24
|
+
"""
|
|
25
|
+
# The first partition that whose mountpoint is a parent of `dir` is
|
|
26
|
+
# returned. Sort the partitions by the length of the mountpoint to ensure
|
|
27
|
+
# that the result is correct. Assume there are partitions with mountpoint
|
|
28
|
+
# `/` and `/home`. This ensures that `/home/foo` is detected as being in
|
|
29
|
+
# the partition with mountpoint `/home`.
|
|
30
|
+
partitions = sorted(
|
|
31
|
+
psutil.disk_partitions(),
|
|
32
|
+
key=lambda part: len(part.mountpoint),
|
|
33
|
+
reverse=True,
|
|
34
|
+
)
|
|
35
|
+
for partition in partitions:
|
|
36
|
+
if dir.is_relative_to(partition.mountpoint):
|
|
37
|
+
return partition
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SystemInfoCommand(QleverCommand):
|
|
42
|
+
def __init__(self):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def description(self) -> str:
|
|
46
|
+
return "Gather some system info to help with troubleshooting"
|
|
47
|
+
|
|
48
|
+
def should_have_qleverfile(self) -> bool:
|
|
49
|
+
return True
|
|
50
|
+
|
|
51
|
+
def relevant_qleverfile_arguments(self) -> dict[str, list[str]]:
|
|
52
|
+
return {"runtime": ["system", "image", "server_container"]}
|
|
53
|
+
|
|
54
|
+
def additional_arguments(self, subparser) -> None:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
def execute(self, args) -> bool:
|
|
58
|
+
# Say what the command is doing.
|
|
59
|
+
self.show(
|
|
60
|
+
"Show system information and Qleverfile", only_show=args.show
|
|
61
|
+
)
|
|
62
|
+
if args.show:
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
# Show system information.
|
|
66
|
+
show_heading("System Information")
|
|
67
|
+
system = platform.system()
|
|
68
|
+
is_linux = system == "Linux"
|
|
69
|
+
is_mac = system == "Darwin"
|
|
70
|
+
is_windows = system == "Windows"
|
|
71
|
+
if is_windows:
|
|
72
|
+
log.warn("Only limited information is gathered on Windows.")
|
|
73
|
+
log.info(f"Version: {version('qlever')} (qlever --version)")
|
|
74
|
+
if is_linux:
|
|
75
|
+
info = platform.freedesktop_os_release()
|
|
76
|
+
log.info(f"OS: {platform.system()} ({info['PRETTY_NAME']})")
|
|
77
|
+
else:
|
|
78
|
+
log.info(f"OS: {platform.system()}")
|
|
79
|
+
log.info(f"Arch: {platform.machine()}")
|
|
80
|
+
log.info(f"Host: {platform.node()}")
|
|
81
|
+
psutil.virtual_memory().total / (1000**3)
|
|
82
|
+
memory_total = psutil.virtual_memory().total / (1024.0**3)
|
|
83
|
+
memory_available = psutil.virtual_memory().available / (1024.0**3)
|
|
84
|
+
log.info(
|
|
85
|
+
f"RAM: {memory_total:.1f} GB total, "
|
|
86
|
+
f"{memory_available:.1f} GB available"
|
|
87
|
+
)
|
|
88
|
+
num_cores = psutil.cpu_count(logical=False)
|
|
89
|
+
num_threads = psutil.cpu_count(logical=True)
|
|
90
|
+
cpu_freq = psutil.cpu_freq().max / 1000
|
|
91
|
+
log.info(
|
|
92
|
+
f"CPU: {num_cores} Cores, "
|
|
93
|
+
f"{num_threads} Threads @ {cpu_freq:.2f} GHz"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
cwd = Path.cwd()
|
|
97
|
+
log.info(f"CWD: {cwd}")
|
|
98
|
+
# Free and total size of the partition on which the current working
|
|
99
|
+
# directory resides.
|
|
100
|
+
disk_usage = psutil.disk_usage(str(cwd))
|
|
101
|
+
partition = get_partition(cwd)
|
|
102
|
+
partition_description = f"{partition.device} @ {partition.mountpoint}"
|
|
103
|
+
fs_type = partition.fstype
|
|
104
|
+
fs_free = format_size(disk_usage.free)
|
|
105
|
+
fs_total = format_size(disk_usage.total)
|
|
106
|
+
log.info(
|
|
107
|
+
f"Disk space in {partition_description} is "
|
|
108
|
+
f"({fs_type}): {fs_free} free / {fs_total} total"
|
|
109
|
+
)
|
|
110
|
+
# User/Group on host and in container
|
|
111
|
+
if is_linux or is_mac:
|
|
112
|
+
user_info = run_command("id", return_output=True).strip()
|
|
113
|
+
log.info(f"User and group on host: {user_info}")
|
|
114
|
+
elif is_windows:
|
|
115
|
+
user_info = run_command("whoami /all", return_output=True).strip()
|
|
116
|
+
log.info(f"User and group on host: {user_info}")
|
|
117
|
+
if args.system in Containerize.supported_systems():
|
|
118
|
+
user_info = Containerize.run_in_container("id", args).strip()
|
|
119
|
+
log.info(f"User and group in container: {user_info}")
|
|
120
|
+
|
|
121
|
+
# Show Qleverfile.
|
|
122
|
+
log.info("")
|
|
123
|
+
show_heading("Contents of Qleverfile")
|
|
124
|
+
qleverfile = cwd / "Qleverfile"
|
|
125
|
+
if qleverfile.exists():
|
|
126
|
+
# TODO: output the effective qlever file using primites from #57
|
|
127
|
+
log.info(qleverfile.read_text())
|
|
128
|
+
else:
|
|
129
|
+
log.info("No Qleverfile found")
|
|
130
|
+
return True
|
qlever/commands/ui.py
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from os import environ
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
|
|
8
|
+
from qlever.command import QleverCommand
|
|
9
|
+
from qlever.containerize import Containerize
|
|
10
|
+
from qlever.log import log
|
|
11
|
+
from qlever.util import is_port_used, run_command
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Return a YAML string for the given dictionary. Format values with
|
|
15
|
+
# newlines using the "|" style.
|
|
16
|
+
def dict_to_yaml(dictionary: dict) -> str:
|
|
17
|
+
"""
|
|
18
|
+
Custom representer for yaml, which uses the "|" style only for
|
|
19
|
+
multiline strings.
|
|
20
|
+
|
|
21
|
+
NOTE: We replace all `\r\n` with `\n` because otherwise the `|` style
|
|
22
|
+
does not work as expected.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
class MultiLineDumper(yaml.SafeDumper):
|
|
26
|
+
def represent_scalar(self, tag, value, style=None):
|
|
27
|
+
value = value.replace("\r\n", "\n")
|
|
28
|
+
if isinstance(value, str) and "\n" in value:
|
|
29
|
+
style = "|"
|
|
30
|
+
return super().represent_scalar(tag, value, style)
|
|
31
|
+
|
|
32
|
+
# Dump as yaml.
|
|
33
|
+
return yaml.dump(
|
|
34
|
+
dictionary,
|
|
35
|
+
sort_keys=False,
|
|
36
|
+
allow_unicode=True,
|
|
37
|
+
Dumper=MultiLineDumper,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class UiCommand(QleverCommand):
|
|
42
|
+
"""
|
|
43
|
+
Class for launching the QLever UI web application.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
def description(self) -> str:
|
|
50
|
+
return "Launch the QLever UI web application"
|
|
51
|
+
|
|
52
|
+
def should_have_qleverfile(self) -> bool:
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
def relevant_qleverfile_arguments(self) -> dict[str, list[str]]:
|
|
56
|
+
return {
|
|
57
|
+
"data": ["name"],
|
|
58
|
+
"server": ["host_name", "port"],
|
|
59
|
+
"ui": [
|
|
60
|
+
"ui_port",
|
|
61
|
+
"ui_config",
|
|
62
|
+
"ui_system",
|
|
63
|
+
"ui_image",
|
|
64
|
+
"ui_container",
|
|
65
|
+
],
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
def additional_arguments(self, subparser) -> None:
|
|
69
|
+
subparser.add_argument(
|
|
70
|
+
"--ui-config-file",
|
|
71
|
+
default="Qleverfile-ui.yml",
|
|
72
|
+
help="Name of the config file for the QLever UI "
|
|
73
|
+
"(default: Qleverfile-ui.yml)",
|
|
74
|
+
)
|
|
75
|
+
subparser.add_argument(
|
|
76
|
+
"--ui-db-file",
|
|
77
|
+
help="Name of the database file for the QLever UI "
|
|
78
|
+
"(default: {name}.ui-db.sqlite3)",
|
|
79
|
+
)
|
|
80
|
+
subparser.add_argument(
|
|
81
|
+
"--no-pull-latest",
|
|
82
|
+
action="store_true",
|
|
83
|
+
default=False,
|
|
84
|
+
help="Do not pull the latest image for the QLever UI "
|
|
85
|
+
"(default: pull the latest image if image name contains '/')",
|
|
86
|
+
)
|
|
87
|
+
subparser.add_argument(
|
|
88
|
+
"--stop",
|
|
89
|
+
action="store_true",
|
|
90
|
+
default=False,
|
|
91
|
+
help="Stop the running container",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def execute(self, args) -> bool:
|
|
95
|
+
# If QLEVER_OVERRIDE_DISABLE_UI is set, this command is disabled.
|
|
96
|
+
qlever_is_running_in_container = environ.get(
|
|
97
|
+
"QLEVER_IS_RUNNING_IN_CONTAINER"
|
|
98
|
+
)
|
|
99
|
+
if qlever_is_running_in_container:
|
|
100
|
+
log.error(
|
|
101
|
+
"The environment variable `QLEVER_OVERRIDE_DISABLE_UI` is set, "
|
|
102
|
+
"therefore `qlever ui` is not available (it should not be called "
|
|
103
|
+
"from inside a container)"
|
|
104
|
+
)
|
|
105
|
+
log.info("")
|
|
106
|
+
if not args.show:
|
|
107
|
+
log.info(
|
|
108
|
+
"For your information, showing the commands that are "
|
|
109
|
+
"executed when `qlever ui` is available:"
|
|
110
|
+
)
|
|
111
|
+
log.info("")
|
|
112
|
+
|
|
113
|
+
# Construct commands and show them.
|
|
114
|
+
pull_latest_image = "/" in args.ui_image and not args.no_pull_latest
|
|
115
|
+
ui_config_name = args.ui_config
|
|
116
|
+
ui_db_file = args.ui_db_file or f"{args.name}.ui-db.sqlite3"
|
|
117
|
+
ui_db_file_from_image = "qleverui.sqlite3"
|
|
118
|
+
ui_config_file = args.ui_config_file
|
|
119
|
+
sparql_endpoint = f"http://{args.host_name}:{args.port}"
|
|
120
|
+
ui_url = f"http://{args.host_name}:{args.ui_port}"
|
|
121
|
+
pull_cmd = f"{args.ui_system} pull -q {args.ui_image}"
|
|
122
|
+
get_db_cmd = (
|
|
123
|
+
f"{args.ui_system} create "
|
|
124
|
+
f"--name {args.ui_container} "
|
|
125
|
+
f"{args.ui_image} "
|
|
126
|
+
f"&& {args.ui_system} cp "
|
|
127
|
+
f"{args.ui_container}:/app/db/{ui_db_file_from_image} {ui_db_file} "
|
|
128
|
+
f"&& {args.ui_system} rm -f {args.ui_container}"
|
|
129
|
+
)
|
|
130
|
+
start_ui_cmd = (
|
|
131
|
+
f"{args.ui_system} run -d "
|
|
132
|
+
f"--volume $(pwd):/app/db "
|
|
133
|
+
f"--env QLEVERUI_DATABASE_URL=sqlite:////app/db/{ui_db_file} "
|
|
134
|
+
f"--publish {args.ui_port}:7000 "
|
|
135
|
+
f"--name {args.ui_container} "
|
|
136
|
+
f"{args.ui_image}"
|
|
137
|
+
)
|
|
138
|
+
get_config_cmd = (
|
|
139
|
+
f"{args.ui_system} exec -i "
|
|
140
|
+
f"{args.ui_container} "
|
|
141
|
+
f'bash -c "python manage.py config {ui_config_name}"'
|
|
142
|
+
)
|
|
143
|
+
set_config_cmd = (
|
|
144
|
+
f"{args.ui_system} exec -i "
|
|
145
|
+
f"{args.ui_container} "
|
|
146
|
+
f'bash -c "python manage.py config {ui_config_name} '
|
|
147
|
+
f'/app/db/{ui_config_file} --hide-all-other-backends"'
|
|
148
|
+
)
|
|
149
|
+
commands_to_show = []
|
|
150
|
+
if not args.stop:
|
|
151
|
+
if pull_latest_image:
|
|
152
|
+
commands_to_show.append(pull_cmd)
|
|
153
|
+
if not Path(ui_db_file).exists():
|
|
154
|
+
commands_to_show.append(get_db_cmd)
|
|
155
|
+
commands_to_show.append(start_ui_cmd)
|
|
156
|
+
if not Path(ui_config_file).exists():
|
|
157
|
+
commands_to_show.append(get_config_cmd)
|
|
158
|
+
else:
|
|
159
|
+
commands_to_show.append(set_config_cmd)
|
|
160
|
+
self.show("\n".join(commands_to_show), only_show=args.show)
|
|
161
|
+
if qlever_is_running_in_container:
|
|
162
|
+
return False
|
|
163
|
+
if args.show:
|
|
164
|
+
return True
|
|
165
|
+
|
|
166
|
+
# Stop running containers.
|
|
167
|
+
was_found_and_stopped = False
|
|
168
|
+
for container_system in Containerize.supported_systems():
|
|
169
|
+
was_found_and_stopped |= Containerize.stop_and_remove_container(
|
|
170
|
+
container_system, args.ui_container
|
|
171
|
+
)
|
|
172
|
+
if was_found_and_stopped:
|
|
173
|
+
log.debug(f"Stopped and removed container `{args.ui_container}`")
|
|
174
|
+
else:
|
|
175
|
+
log.debug(f"No container with name `{args.ui_container}` found")
|
|
176
|
+
if args.stop:
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
# Pull the latest image.
|
|
180
|
+
if pull_latest_image:
|
|
181
|
+
log.debug(f"Pulling image `{args.ui_image}` for QLever UI")
|
|
182
|
+
run_command(pull_cmd)
|
|
183
|
+
|
|
184
|
+
# Check if the UI port is already being used.
|
|
185
|
+
if is_port_used(args.ui_port):
|
|
186
|
+
log.warning(
|
|
187
|
+
f"It looks like port {args.ui_port} for the QLever UI "
|
|
188
|
+
f"is already in use. You can set another port in the "
|
|
189
|
+
f" Qleverfile in the [ui] section with the UI_PORT variable."
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Get the QLever UI database from the image, unless it already exists.
|
|
193
|
+
if Path(ui_db_file).exists():
|
|
194
|
+
log.debug(f"Found QLever UI database `{ui_db_file}`, reusing it")
|
|
195
|
+
else:
|
|
196
|
+
log.debug(f"Getting QLever UI database `{ui_db_file}` from image")
|
|
197
|
+
try:
|
|
198
|
+
run_command(get_db_cmd)
|
|
199
|
+
except Exception as e:
|
|
200
|
+
log.error(
|
|
201
|
+
f"Failed to get {ui_db_file} from {args.ui_image} "
|
|
202
|
+
f"({e})"
|
|
203
|
+
)
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
# Start the QLever UI.
|
|
207
|
+
try:
|
|
208
|
+
log.debug(
|
|
209
|
+
f"Starting new container with name `{args.ui_container}`"
|
|
210
|
+
)
|
|
211
|
+
run_command(start_ui_cmd)
|
|
212
|
+
except Exception as e:
|
|
213
|
+
log.error(f"Failed to start container `{args.ui_container}` ({e})")
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
# Check if config file with name `ui_config_file` exists. If not, try
|
|
217
|
+
# to obtain it via `get_config_cmd` and set it as default.
|
|
218
|
+
if Path(ui_config_file).exists():
|
|
219
|
+
log.info(f"Found config file `{ui_config_file}` and reusing it")
|
|
220
|
+
else:
|
|
221
|
+
try:
|
|
222
|
+
log.info(
|
|
223
|
+
f"Get default config file `{ui_config_file}` from image "
|
|
224
|
+
f"`{args.ui_image}` and set endpoint to `{sparql_endpoint}`"
|
|
225
|
+
)
|
|
226
|
+
config_yaml = run_command(get_config_cmd, return_output=True)
|
|
227
|
+
config_dict = yaml.safe_load(config_yaml)
|
|
228
|
+
except Exception as e:
|
|
229
|
+
log.error("")
|
|
230
|
+
log.error(
|
|
231
|
+
f"An error occured while getting and parsing the "
|
|
232
|
+
f"config file ({e})"
|
|
233
|
+
)
|
|
234
|
+
return False
|
|
235
|
+
try:
|
|
236
|
+
config_dict["config"]["backend"]["isDefault"] = True
|
|
237
|
+
config_dict["config"]["backend"]["baseUrl"] = sparql_endpoint
|
|
238
|
+
config_dict["config"]["backend"]["sortKey"] = 1
|
|
239
|
+
config_yaml = dict_to_yaml(config_dict)
|
|
240
|
+
with open(ui_config_file, "w") as f:
|
|
241
|
+
f.write(config_yaml)
|
|
242
|
+
except Exception as e:
|
|
243
|
+
log.error("")
|
|
244
|
+
log.error(
|
|
245
|
+
f"An error occured while modifying and writing the "
|
|
246
|
+
f"config file ({e})"
|
|
247
|
+
)
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
# Configure the QLever UI.
|
|
251
|
+
try:
|
|
252
|
+
run_command(set_config_cmd)
|
|
253
|
+
except Exception as e:
|
|
254
|
+
log.error(f"Failed to configure the QLever UI ({e})")
|
|
255
|
+
return False
|
|
256
|
+
|
|
257
|
+
# If we come this far, everything should work.
|
|
258
|
+
log.info("")
|
|
259
|
+
log.info(
|
|
260
|
+
f"The QLever UI should now be up at {ui_url}/{ui_config_name}"
|
|
261
|
+
)
|
|
262
|
+
log.info("")
|
|
263
|
+
log.debug(
|
|
264
|
+
"If you must, you can log in as QLever UI admin with "
|
|
265
|
+
'username and password "demo"'
|
|
266
|
+
)
|
|
267
|
+
log.info(
|
|
268
|
+
f"You can modify the config file at `{ui_config_file}` "
|
|
269
|
+
f"and then just run `qlever ui` again"
|
|
270
|
+
)
|
|
271
|
+
return True
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
import time
|
|
5
|
+
import traceback
|
|
6
|
+
|
|
7
|
+
from qlever.command import QleverCommand
|
|
8
|
+
from qlever.log import log
|
|
9
|
+
from qlever.util import run_command
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UpdateCommand(QleverCommand):
|
|
13
|
+
"""
|
|
14
|
+
Class for executing a SPARQL UPDATE against a SPARQL endpoint.
|
|
15
|
+
|
|
16
|
+
The command accepts the update either directly on the command line or
|
|
17
|
+
via a file path provided with --update-file.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
def description(self) -> str:
|
|
24
|
+
return "Send an update to a SPARQL endpoint"
|
|
25
|
+
|
|
26
|
+
def should_have_qleverfile(self) -> bool:
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
def relevant_qleverfile_arguments(self) -> dict[str,list[str]]:
|
|
30
|
+
return {"server": ["host_name", "port", "access_token"]}
|
|
31
|
+
|
|
32
|
+
def additional_arguments(self, subparser) -> None:
|
|
33
|
+
subparser.add_argument(
|
|
34
|
+
"update",
|
|
35
|
+
type=str,
|
|
36
|
+
nargs="?",
|
|
37
|
+
default=None,
|
|
38
|
+
help="SPARQL UPDATE to send (use --update-file to send from a file)",
|
|
39
|
+
)
|
|
40
|
+
subparser.add_argument(
|
|
41
|
+
"--update-file",
|
|
42
|
+
type=str,
|
|
43
|
+
help="Path to a file containing the SPARQL UPDATE to send",
|
|
44
|
+
)
|
|
45
|
+
subparser.add_argument(
|
|
46
|
+
"--sparql-endpoint", type=str, help="URL of the SPARQL endpoint"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def execute(self, args) -> bool:
|
|
50
|
+
sparql_endpoint = (
|
|
51
|
+
args.sparql_endpoint if args.sparql_endpoint else f"{args.host_name}:{args.port}"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
curl_cmd = (
|
|
55
|
+
f"curl -s {sparql_endpoint} -X POST "
|
|
56
|
+
f"-H 'Authorization: Bearer {args.access_token}' "
|
|
57
|
+
f"-H 'Content-Type: application/sparql-update' "
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if args.update:
|
|
61
|
+
curl_cmd += f"--data-binary {shlex.quote(args.update)}"
|
|
62
|
+
elif args.update_file:
|
|
63
|
+
curl_cmd += f"--data-binary @{shlex.quote(args.update_file)}"
|
|
64
|
+
else:
|
|
65
|
+
log.error("No SPARQL UPDATE provided. Pass it as an argument or via --update-file.")
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
# Show and exit if requested
|
|
69
|
+
self.show(curl_cmd, only_show=args.show)
|
|
70
|
+
if args.show:
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
# Execute update
|
|
74
|
+
try:
|
|
75
|
+
start_time = time.time()
|
|
76
|
+
run_command(curl_cmd)
|
|
77
|
+
time_msecs = round(1000 * (time.time() - start_time))
|
|
78
|
+
if args.log_level != "NO_LOG":
|
|
79
|
+
log.info("")
|
|
80
|
+
log.info(
|
|
81
|
+
f"Update processing time (end-to-end): {time_msecs:,d} ms"
|
|
82
|
+
)
|
|
83
|
+
except Exception as e:
|
|
84
|
+
if args.log_level == "DEBUG":
|
|
85
|
+
traceback.print_exc()
|
|
86
|
+
log.error(e)
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
return True
|
|
90
|
+
|