qalita 2.3.1__py3-none-any.whl → 2.5.2__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.
- qalita/__main__.py +213 -9
- qalita/commands/{agent.py → worker.py} +89 -89
- qalita/internal/config.py +26 -19
- qalita/internal/utils.py +1 -1
- qalita/web/app.py +97 -14
- qalita/web/blueprints/context.py +13 -60
- qalita/web/blueprints/dashboard.py +35 -76
- qalita/web/blueprints/helpers.py +154 -63
- qalita/web/blueprints/sources.py +29 -61
- qalita/web/blueprints/{agents.py → workers.py} +108 -185
- qalita-2.5.2.dist-info/METADATA +66 -0
- qalita-2.5.2.dist-info/RECORD +24 -0
- {qalita-2.3.1.dist-info → qalita-2.5.2.dist-info}/WHEEL +1 -1
- qalita-2.5.2.dist-info/entry_points.txt +2 -0
- qalita/web/blueprints/studio.py +0 -1255
- qalita/web/public/chatgpt.svg +0 -3
- qalita/web/public/claude.png +0 -0
- qalita/web/public/favicon.ico +0 -0
- qalita/web/public/gemini.png +0 -0
- qalita/web/public/logo-no-slogan.png +0 -0
- qalita/web/public/logo-white-no-slogan.svg +0 -11
- qalita/web/public/mistral.svg +0 -1
- qalita/web/public/noise.webp +0 -0
- qalita/web/public/ollama.png +0 -0
- qalita/web/public/platform.png +0 -0
- qalita/web/public/sources-logos/alloy-db.png +0 -0
- qalita/web/public/sources-logos/amazon-athena.png +0 -0
- qalita/web/public/sources-logos/amazon-rds.png +0 -0
- qalita/web/public/sources-logos/api.svg +0 -2
- qalita/web/public/sources-logos/avro.svg +0 -20
- qalita/web/public/sources-logos/azure-database-mysql.png +0 -0
- qalita/web/public/sources-logos/azure-database-postgresql.png +0 -0
- qalita/web/public/sources-logos/azure-sql-database.png +0 -0
- qalita/web/public/sources-logos/azure-sql-managed-instance.png +0 -0
- qalita/web/public/sources-logos/azure-synapse-analytics.png +0 -0
- qalita/web/public/sources-logos/azure_blob.svg +0 -1
- qalita/web/public/sources-logos/bigquery.png +0 -0
- qalita/web/public/sources-logos/cassandra.svg +0 -254
- qalita/web/public/sources-logos/clickhouse.png +0 -0
- qalita/web/public/sources-logos/cloud-sql.png +0 -0
- qalita/web/public/sources-logos/cockroach-db.png +0 -0
- qalita/web/public/sources-logos/csv.svg +0 -1
- qalita/web/public/sources-logos/database.svg +0 -3
- qalita/web/public/sources-logos/databricks.png +0 -0
- qalita/web/public/sources-logos/duckdb.png +0 -0
- qalita/web/public/sources-logos/elasticsearch.svg +0 -1
- qalita/web/public/sources-logos/excel.svg +0 -1
- qalita/web/public/sources-logos/file.svg +0 -1
- qalita/web/public/sources-logos/folder.svg +0 -6
- qalita/web/public/sources-logos/gcs.png +0 -0
- qalita/web/public/sources-logos/hdfs.svg +0 -1
- qalita/web/public/sources-logos/ibm-db2.png +0 -0
- qalita/web/public/sources-logos/json.png +0 -0
- qalita/web/public/sources-logos/maria-db.png +0 -0
- qalita/web/public/sources-logos/mongodb.svg +0 -1
- qalita/web/public/sources-logos/mssql.svg +0 -1
- qalita/web/public/sources-logos/mysql.svg +0 -7
- qalita/web/public/sources-logos/oracle.svg +0 -4
- qalita/web/public/sources-logos/parquet.svg +0 -16
- qalita/web/public/sources-logos/picture.png +0 -0
- qalita/web/public/sources-logos/postgresql.svg +0 -22
- qalita/web/public/sources-logos/questdb.png +0 -0
- qalita/web/public/sources-logos/redshift.png +0 -0
- qalita/web/public/sources-logos/s3.svg +0 -34
- qalita/web/public/sources-logos/sap-hana.png +0 -0
- qalita/web/public/sources-logos/sftp.png +0 -0
- qalita/web/public/sources-logos/single-store.png +0 -0
- qalita/web/public/sources-logos/snowflake.png +0 -0
- qalita/web/public/sources-logos/sqlite.svg +0 -104
- qalita/web/public/sources-logos/sqlserver.png +0 -0
- qalita/web/public/sources-logos/starburst.png +0 -0
- qalita/web/public/sources-logos/stream.png +0 -0
- qalita/web/public/sources-logos/teradata.png +0 -0
- qalita/web/public/sources-logos/timescale.png +0 -0
- qalita/web/public/sources-logos/xls.svg +0 -1
- qalita/web/public/sources-logos/xlsx.svg +0 -1
- qalita/web/public/sources-logos/yugabyte-db.png +0 -0
- qalita/web/public/studio-logo.svg +0 -10
- qalita/web/public/studio.css +0 -304
- qalita/web/public/studio.png +0 -0
- qalita/web/public/styles.css +0 -682
- qalita/web/templates/dashboard.html +0 -373
- qalita/web/templates/navbar.html +0 -40
- qalita/web/templates/sources/added.html +0 -57
- qalita/web/templates/sources/edit.html +0 -411
- qalita/web/templates/sources/select-source.html +0 -128
- qalita/web/templates/studio/agent-panel.html +0 -769
- qalita/web/templates/studio/context-panel.html +0 -300
- qalita/web/templates/studio/index.html +0 -79
- qalita/web/templates/studio/navbar.html +0 -14
- qalita/web/templates/studio/view-panel.html +0 -529
- qalita-2.3.1.dist-info/METADATA +0 -58
- qalita-2.3.1.dist-info/RECORD +0 -101
- qalita-2.3.1.dist-info/entry_points.txt +0 -3
- {qalita-2.3.1.dist-info → qalita-2.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,40 +10,40 @@ from flask import Blueprint, jsonify, current_app
|
|
|
10
10
|
from qalita.internal.utils import logger, validate_token
|
|
11
11
|
from qalita.internal.request import send_request
|
|
12
12
|
from .helpers import (
|
|
13
|
-
|
|
13
|
+
worker_status_payload,
|
|
14
14
|
read_selected_env,
|
|
15
15
|
parse_env_file,
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
worker_log_file_path,
|
|
17
|
+
worker_pid_file_path,
|
|
18
18
|
open_path_in_file_explorer,
|
|
19
|
-
|
|
19
|
+
compute_worker_summary,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
bp = Blueprint("
|
|
23
|
+
bp = Blueprint("workers", __name__)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
@bp.get("/
|
|
27
|
-
def
|
|
28
|
-
return jsonify(
|
|
26
|
+
@bp.get("/worker/status")
|
|
27
|
+
def worker_status():
|
|
28
|
+
return jsonify(worker_status_payload())
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
@bp.post("/
|
|
32
|
-
def
|
|
31
|
+
@bp.post("/worker/start")
|
|
32
|
+
def worker_start():
|
|
33
33
|
cfg = current_app.config["QALITA_CONFIG_OBJ"]
|
|
34
|
-
|
|
34
|
+
worker_name = None
|
|
35
35
|
|
|
36
|
-
st =
|
|
36
|
+
st = worker_status_payload()
|
|
37
37
|
if st.get("running"):
|
|
38
38
|
return jsonify({"ok": True, "already_running": True, **st})
|
|
39
39
|
|
|
40
40
|
try:
|
|
41
41
|
sel_path = read_selected_env()
|
|
42
42
|
if sel_path and os.path.isfile(sel_path):
|
|
43
|
-
logger.info(f"
|
|
43
|
+
logger.info(f"worker_start: applying selected env at [{sel_path}]")
|
|
44
44
|
_login_with_env(sel_path)
|
|
45
45
|
except Exception as exc:
|
|
46
|
-
logger.error(f"
|
|
46
|
+
logger.error(f"worker_start: failed applying selected env: {exc}")
|
|
47
47
|
return (
|
|
48
48
|
jsonify({"ok": False, "error": f"Failed to apply selected context: {exc}"}),
|
|
49
49
|
400,
|
|
@@ -53,32 +53,32 @@ def agent_start():
|
|
|
53
53
|
sel_path = read_selected_env()
|
|
54
54
|
if sel_path and os.path.isfile(sel_path):
|
|
55
55
|
data = parse_env_file(sel_path)
|
|
56
|
-
|
|
57
|
-
data.get("
|
|
56
|
+
worker_name = (
|
|
57
|
+
data.get("QALITA_WORKER_NAME")
|
|
58
58
|
or data.get("AGENT_NAME")
|
|
59
59
|
or data.get("NAME")
|
|
60
60
|
or None
|
|
61
61
|
)
|
|
62
62
|
except Exception:
|
|
63
|
-
|
|
64
|
-
if not
|
|
63
|
+
worker_name = None
|
|
64
|
+
if not worker_name:
|
|
65
65
|
try:
|
|
66
|
-
|
|
66
|
+
worker_name = getattr(cfg, "name", None) or None
|
|
67
67
|
except Exception:
|
|
68
|
-
|
|
69
|
-
if not
|
|
68
|
+
worker_name = None
|
|
69
|
+
if not worker_name:
|
|
70
70
|
try:
|
|
71
|
-
raw = cfg.
|
|
71
|
+
raw = cfg.load_worker_config()
|
|
72
72
|
if isinstance(raw, dict) and raw:
|
|
73
|
-
|
|
73
|
+
worker_name = (
|
|
74
74
|
(raw.get("context", {}).get("remote", {}) or {}).get("name")
|
|
75
75
|
or raw.get("name")
|
|
76
76
|
or None
|
|
77
77
|
)
|
|
78
78
|
except Exception:
|
|
79
|
-
|
|
80
|
-
if not
|
|
81
|
-
|
|
79
|
+
worker_name = None
|
|
80
|
+
if not worker_name:
|
|
81
|
+
worker_name = "agent"
|
|
82
82
|
|
|
83
83
|
# Ensure we have minimum credentials before preflight
|
|
84
84
|
if not getattr(cfg, "token", None) or not getattr(cfg, "url", None):
|
|
@@ -144,7 +144,7 @@ def agent_start():
|
|
|
144
144
|
env["QALITA_HOME"] = cfg.qalita_home # type: ignore[attr-defined]
|
|
145
145
|
except Exception:
|
|
146
146
|
env["QALITA_HOME"] = os.path.expanduser("~/.qalita")
|
|
147
|
-
logger.info(f"
|
|
147
|
+
logger.info(f"worker_start: QALITA_HOME resolved to [{env.get('QALITA_HOME')}]")
|
|
148
148
|
try:
|
|
149
149
|
sel_path = read_selected_env()
|
|
150
150
|
if sel_path and os.path.isfile(sel_path):
|
|
@@ -161,12 +161,12 @@ def agent_start():
|
|
|
161
161
|
except Exception:
|
|
162
162
|
pass
|
|
163
163
|
try:
|
|
164
|
-
if not any(env.get(k) for k in ("
|
|
165
|
-
env["
|
|
164
|
+
if not any(env.get(k) for k in ("QALITA_WORKER_MODE", "AGENT_MODE", "MODE")):
|
|
165
|
+
env["QALITA_WORKER_MODE"] = "worker"
|
|
166
166
|
except Exception:
|
|
167
|
-
env["
|
|
167
|
+
env["QALITA_WORKER_MODE"] = "worker"
|
|
168
168
|
logger.info(
|
|
169
|
-
f"
|
|
169
|
+
f"worker_start: effective agent mode is [{env.get('QALITA_WORKER_MODE') or env.get('AGENT_MODE') or env.get('MODE')}]"
|
|
170
170
|
)
|
|
171
171
|
if os.name == "nt":
|
|
172
172
|
python_bin = sys.executable or "python"
|
|
@@ -174,23 +174,23 @@ def agent_start():
|
|
|
174
174
|
python_bin,
|
|
175
175
|
"-m",
|
|
176
176
|
"qalita",
|
|
177
|
-
"
|
|
177
|
+
"worker",
|
|
178
178
|
"-n",
|
|
179
|
-
str(
|
|
179
|
+
str(worker_name),
|
|
180
180
|
"-m",
|
|
181
181
|
"worker",
|
|
182
182
|
"run",
|
|
183
183
|
]
|
|
184
|
-
logger.info(f"
|
|
184
|
+
logger.info(f"worker_start: using python interpreter [{python_bin}]")
|
|
185
185
|
else:
|
|
186
|
-
cmd = ["qalita", "
|
|
187
|
-
logger.info(f"
|
|
188
|
-
log_path =
|
|
186
|
+
cmd = ["qalita", "worker", "-n", str(worker_name), "-m", "worker", "run"]
|
|
187
|
+
logger.info(f"worker_start: executing command: {' '.join(cmd)}")
|
|
188
|
+
log_path = worker_log_file_path()
|
|
189
189
|
try:
|
|
190
190
|
os.makedirs(os.path.dirname(log_path), exist_ok=True)
|
|
191
191
|
except Exception:
|
|
192
192
|
pass
|
|
193
|
-
logger.info(f"
|
|
193
|
+
logger.info(f"worker_start: logging to [{log_path}]")
|
|
194
194
|
log_file = open(log_path, "a", encoding="utf-8", buffering=1)
|
|
195
195
|
popen_kwargs = {"stdout": log_file, "stderr": log_file, "env": env}
|
|
196
196
|
if os.name == "nt":
|
|
@@ -202,23 +202,23 @@ def agent_start():
|
|
|
202
202
|
DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW
|
|
203
203
|
)
|
|
204
204
|
logger.info(
|
|
205
|
-
"
|
|
205
|
+
"worker_start: using Windows creation flags DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW"
|
|
206
206
|
)
|
|
207
207
|
except Exception:
|
|
208
208
|
pass
|
|
209
209
|
else:
|
|
210
210
|
popen_kwargs["start_new_session"] = True
|
|
211
|
-
logger.info("
|
|
211
|
+
logger.info("worker_start: using POSIX start_new_session=True")
|
|
212
212
|
proc = subprocess.Popen(cmd, **popen_kwargs)
|
|
213
213
|
try:
|
|
214
|
-
with open(
|
|
214
|
+
with open(worker_pid_file_path(), "w", encoding="utf-8") as f:
|
|
215
215
|
f.write(str(proc.pid))
|
|
216
216
|
except Exception:
|
|
217
217
|
pass
|
|
218
|
-
logger.info(f"
|
|
218
|
+
logger.info(f"worker_start: started process with PID [{proc.pid}]")
|
|
219
219
|
return jsonify({"ok": True, "pid": proc.pid, "login_ok": True})
|
|
220
220
|
except Exception as exc:
|
|
221
|
-
logger.error(f"
|
|
221
|
+
logger.error(f"worker_start: primary launch failed: {exc}")
|
|
222
222
|
try:
|
|
223
223
|
env = dict(os.environ)
|
|
224
224
|
try:
|
|
@@ -226,7 +226,7 @@ def agent_start():
|
|
|
226
226
|
except Exception:
|
|
227
227
|
env["QALITA_HOME"] = os.path.expanduser("~/.qalita")
|
|
228
228
|
logger.info(
|
|
229
|
-
f"
|
|
229
|
+
f"worker_start(fallback): QALITA_HOME resolved to [{env.get('QALITA_HOME')}]"
|
|
230
230
|
)
|
|
231
231
|
sel_path = read_selected_env()
|
|
232
232
|
if sel_path and os.path.isfile(sel_path):
|
|
@@ -242,33 +242,33 @@ def agent_start():
|
|
|
242
242
|
env[k] = v
|
|
243
243
|
try:
|
|
244
244
|
if not any(
|
|
245
|
-
env.get(k) for k in ("
|
|
245
|
+
env.get(k) for k in ("QALITA_WORKER_MODE", "AGENT_MODE", "MODE")
|
|
246
246
|
):
|
|
247
|
-
env["
|
|
247
|
+
env["QALITA_WORKER_MODE"] = "worker"
|
|
248
248
|
except Exception:
|
|
249
|
-
env["
|
|
249
|
+
env["QALITA_WORKER_MODE"] = "worker"
|
|
250
250
|
logger.info(
|
|
251
|
-
f"
|
|
251
|
+
f"worker_start(fallback): effective agent mode is [{env.get('QALITA_WORKER_MODE') or env.get('AGENT_MODE') or env.get('MODE')}]"
|
|
252
252
|
)
|
|
253
253
|
python_bin = sys.executable or "python3"
|
|
254
254
|
cmd = [
|
|
255
255
|
python_bin,
|
|
256
256
|
"-m",
|
|
257
257
|
"qalita",
|
|
258
|
-
"
|
|
258
|
+
"worker",
|
|
259
259
|
"-n",
|
|
260
|
-
str(
|
|
260
|
+
str(worker_name),
|
|
261
261
|
"-m",
|
|
262
262
|
"worker",
|
|
263
263
|
"run",
|
|
264
264
|
]
|
|
265
|
-
logger.info(f"
|
|
266
|
-
log_path =
|
|
265
|
+
logger.info(f"worker_start(fallback): executing command: {' '.join(cmd)}")
|
|
266
|
+
log_path = worker_log_file_path()
|
|
267
267
|
try:
|
|
268
268
|
os.makedirs(os.path.dirname(log_path), exist_ok=True)
|
|
269
269
|
except Exception:
|
|
270
270
|
pass
|
|
271
|
-
logger.info(f"
|
|
271
|
+
logger.info(f"worker_start(fallback): logging to [{log_path}]")
|
|
272
272
|
log_file = open(log_path, "a", encoding="utf-8", buffering=1)
|
|
273
273
|
popen_kwargs = {"stdout": log_file, "stderr": log_file, "env": env}
|
|
274
274
|
if os.name == "nt":
|
|
@@ -280,31 +280,31 @@ def agent_start():
|
|
|
280
280
|
DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW
|
|
281
281
|
)
|
|
282
282
|
logger.info(
|
|
283
|
-
"
|
|
283
|
+
"worker_start(fallback): using Windows creation flags DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW"
|
|
284
284
|
)
|
|
285
285
|
except Exception:
|
|
286
286
|
pass
|
|
287
287
|
else:
|
|
288
288
|
popen_kwargs["start_new_session"] = True
|
|
289
|
-
logger.info("
|
|
289
|
+
logger.info("worker_start(fallback): using POSIX start_new_session=True")
|
|
290
290
|
proc = subprocess.Popen(cmd, **popen_kwargs)
|
|
291
|
-
with open(
|
|
291
|
+
with open(worker_pid_file_path(), "w", encoding="utf-8") as f:
|
|
292
292
|
f.write(str(proc.pid))
|
|
293
|
-
logger.info(f"
|
|
293
|
+
logger.info(f"worker_start(fallback): started process with PID [{proc.pid}]")
|
|
294
294
|
return jsonify(
|
|
295
295
|
{"ok": True, "pid": proc.pid, "fallback": True, "login_ok": True}
|
|
296
296
|
)
|
|
297
297
|
except Exception as exc2:
|
|
298
|
-
logger.error(f"
|
|
298
|
+
logger.error(f"worker_start(fallback): launch failed: {exc2}")
|
|
299
299
|
return (
|
|
300
300
|
jsonify({"ok": False, "error": f"{exc}", "fallback_error": f"{exc2}"}),
|
|
301
301
|
500,
|
|
302
302
|
)
|
|
303
303
|
|
|
304
304
|
|
|
305
|
-
@bp.post("/
|
|
306
|
-
def
|
|
307
|
-
pid =
|
|
305
|
+
@bp.post("/worker/stop")
|
|
306
|
+
def worker_stop():
|
|
307
|
+
pid = _read_worker_pid()
|
|
308
308
|
if not pid:
|
|
309
309
|
return jsonify({"ok": True, "already_stopped": True})
|
|
310
310
|
if os.name == "nt":
|
|
@@ -328,7 +328,7 @@ def agent_stop():
|
|
|
328
328
|
except Exception:
|
|
329
329
|
pass
|
|
330
330
|
try:
|
|
331
|
-
p =
|
|
331
|
+
p = worker_pid_file_path()
|
|
332
332
|
if os.path.exists(p):
|
|
333
333
|
os.remove(p)
|
|
334
334
|
except Exception:
|
|
@@ -336,138 +336,61 @@ def agent_stop():
|
|
|
336
336
|
return jsonify({"ok": True})
|
|
337
337
|
|
|
338
338
|
|
|
339
|
-
@bp.get("/
|
|
340
|
-
def
|
|
339
|
+
@bp.get("/worker/summary")
|
|
340
|
+
def worker_summary():
|
|
341
341
|
cfg = current_app.config["QALITA_CONFIG_OBJ"]
|
|
342
|
-
|
|
343
|
-
return jsonify({"
|
|
342
|
+
worker_conf, worker_runs = compute_worker_summary(cfg)
|
|
343
|
+
return jsonify({"worker_conf": worker_conf or {}, "worker_runs": worker_runs})
|
|
344
344
|
|
|
345
345
|
|
|
346
|
-
@bp.get("/
|
|
347
|
-
def
|
|
346
|
+
@bp.get("/worker/run/<run_name>")
|
|
347
|
+
def open_worker_run(run_name: str):
|
|
348
348
|
cfg = current_app.config["QALITA_CONFIG_OBJ"]
|
|
349
|
-
run_root = cfg.
|
|
350
|
-
import os
|
|
349
|
+
run_root = cfg.get_worker_run_path()
|
|
351
350
|
|
|
352
351
|
candidate = os.path.normpath(os.path.join(run_root, run_name))
|
|
353
352
|
if not candidate.startswith(os.path.normpath(run_root) + os.sep):
|
|
354
|
-
return (
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
<body style='background-repeat: no-repeat;'>
|
|
362
|
-
<div class="container">
|
|
363
|
-
<h2>Agent Run - {candidate}</h2>
|
|
364
|
-
<div class="card">
|
|
365
|
-
<h1>Invalid path</h1>
|
|
366
|
-
<p><a href='/'>Back to Dashboard</a></p>
|
|
367
|
-
<hr/>
|
|
368
|
-
<p>If the path is invalid it means :</p>
|
|
369
|
-
<ul>
|
|
370
|
-
<li>Your local agent is not the one that ran the analysis you are trying to get files from</li>
|
|
371
|
-
<li>The job failed or was cancelled</li>
|
|
372
|
-
</ul>
|
|
373
|
-
</div>
|
|
374
|
-
</div>
|
|
375
|
-
<div class="footer">
|
|
376
|
-
<div class="inner">
|
|
377
|
-
<div>
|
|
378
|
-
<span>© QALITA</span> — QALITA CLI
|
|
379
|
-
</div>
|
|
380
|
-
<div>
|
|
381
|
-
<span id="cli_version"></span>
|
|
382
|
-
</div>
|
|
383
|
-
</div>
|
|
384
|
-
</div>
|
|
385
|
-
</body></html>
|
|
386
|
-
""",
|
|
387
|
-
400,
|
|
388
|
-
)
|
|
353
|
+
return jsonify({
|
|
354
|
+
"ok": False,
|
|
355
|
+
"error": "Invalid path",
|
|
356
|
+
"path": candidate,
|
|
357
|
+
"message": "If the path is invalid it means: Your local agent is not the one that ran the analysis you are trying to get files from, or the job failed or was cancelled.",
|
|
358
|
+
}), 400
|
|
359
|
+
|
|
389
360
|
if not os.path.isdir(candidate):
|
|
390
|
-
return (
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
<body style='background-repeat: no-repeat;'>
|
|
398
|
-
<div class="container">
|
|
399
|
-
<h2>Agent Run - {candidate}</h2>
|
|
400
|
-
<div class="card">
|
|
401
|
-
<h1>Run folder not found</h1>
|
|
402
|
-
<p><a href='/'>Back to Dashboard</a></p>
|
|
403
|
-
<hr/>
|
|
404
|
-
<p>If the run folder does not exist it means :</p>
|
|
405
|
-
<ul>
|
|
406
|
-
<li>Your local agent is not the one that ran the analysis you are trying to get files from</li>
|
|
407
|
-
<li>The job failed or was cancelled</li>
|
|
408
|
-
</ul>
|
|
409
|
-
</div>
|
|
410
|
-
</div>
|
|
411
|
-
<div class="footer">
|
|
412
|
-
<div class="inner">
|
|
413
|
-
<div>
|
|
414
|
-
<span>© QALITA</span> — QALITA CLI
|
|
415
|
-
</div>
|
|
416
|
-
<div>
|
|
417
|
-
<span id="cli_version"></span>
|
|
418
|
-
</div>
|
|
419
|
-
</div>
|
|
420
|
-
</div>
|
|
421
|
-
</body></html>
|
|
422
|
-
""",
|
|
423
|
-
404,
|
|
424
|
-
)
|
|
361
|
+
return jsonify({
|
|
362
|
+
"ok": False,
|
|
363
|
+
"error": "Run folder not found",
|
|
364
|
+
"path": candidate,
|
|
365
|
+
"message": "If the run folder does not exist it means: Your local agent is not the one that ran the analysis you are trying to get files from, or the job failed or was cancelled.",
|
|
366
|
+
}), 404
|
|
367
|
+
|
|
425
368
|
ok, method_used = open_path_in_file_explorer(candidate)
|
|
426
369
|
status = "Opened" if ok else "Could not open automatically"
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
{
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
<li>Linux: use your file manager or run: <code>xdg-open {candidate}</code></li>
|
|
447
|
-
<li>Windows/WSL: use Explorer at <code>\\\\wsl$\\{os.environ.get('WSL_DISTRO_NAME','<distro>')}\\{candidate}</code></li>
|
|
448
|
-
</ul>
|
|
449
|
-
</div>
|
|
450
|
-
</div>
|
|
451
|
-
<div class="footer">
|
|
452
|
-
<div class="inner">
|
|
453
|
-
<div>
|
|
454
|
-
<span>© QALITA</span> — QALITA CLI
|
|
455
|
-
</div>
|
|
456
|
-
<div>
|
|
457
|
-
<span id="cli_version"></span>
|
|
458
|
-
</div>
|
|
459
|
-
</div>
|
|
460
|
-
</div>
|
|
461
|
-
</body></html>
|
|
462
|
-
""",
|
|
463
|
-
200,
|
|
464
|
-
{"Content-Type": "text/html; charset=utf-8"},
|
|
465
|
-
)
|
|
370
|
+
|
|
371
|
+
# Build instructions for manual navigation
|
|
372
|
+
instructions = []
|
|
373
|
+
if sys.platform == "darwin":
|
|
374
|
+
instructions.append("macOS: open Finder and Go to Folder…")
|
|
375
|
+
elif sys.platform.startswith("linux"):
|
|
376
|
+
instructions.append(f"Linux: use your file manager or run: xdg-open {candidate}")
|
|
377
|
+
elif sys.platform.startswith("win") or "WSL" in os.environ:
|
|
378
|
+
wsl_distro = os.environ.get('WSL_DISTRO_NAME', '<distro>')
|
|
379
|
+
instructions.append(f"Windows/WSL: use Explorer at \\\\wsl$\\{wsl_distro}\\{candidate}")
|
|
380
|
+
|
|
381
|
+
return jsonify({
|
|
382
|
+
"ok": True,
|
|
383
|
+
"status": status,
|
|
384
|
+
"path": candidate,
|
|
385
|
+
"method": method_used,
|
|
386
|
+
"instructions": instructions,
|
|
387
|
+
"message": f"{status} file explorer. If the explorer did not open, you can navigate to this folder manually.",
|
|
388
|
+
})
|
|
466
389
|
|
|
467
390
|
|
|
468
|
-
def
|
|
391
|
+
def _read_worker_pid():
|
|
469
392
|
try:
|
|
470
|
-
with open(
|
|
393
|
+
with open(worker_pid_file_path(), "r", encoding="utf-8") as f:
|
|
471
394
|
raw = f.read().strip()
|
|
472
395
|
return int(raw) if raw else None
|
|
473
396
|
except Exception:
|
|
@@ -485,19 +408,19 @@ def _login_with_env(env_path: str) -> None:
|
|
|
485
408
|
return default
|
|
486
409
|
|
|
487
410
|
name = (
|
|
488
|
-
pick("
|
|
411
|
+
pick("QALITA_WORKER_NAME", "AGENT_NAME", "NAME")
|
|
489
412
|
or getattr(cfg, "name", None)
|
|
490
413
|
or "agent"
|
|
491
414
|
)
|
|
492
415
|
mode = (
|
|
493
|
-
pick("
|
|
416
|
+
pick("QALITA_WORKER_MODE", "AGENT_MODE", "MODE")
|
|
494
417
|
or getattr(cfg, "mode", None)
|
|
495
418
|
or "job"
|
|
496
419
|
)
|
|
497
|
-
token = pick("
|
|
420
|
+
token = pick("QALITA_WORKER_TOKEN", "QALITA_TOKEN", "TOKEN") or getattr(
|
|
498
421
|
cfg, "token", None
|
|
499
422
|
)
|
|
500
|
-
url = pick("
|
|
423
|
+
url = pick("QALITA_WORKER_ENDPOINT", "QALITA_URL", "URL") or getattr(
|
|
501
424
|
cfg, "url", None
|
|
502
425
|
)
|
|
503
426
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qalita
|
|
3
|
+
Version: 2.5.2
|
|
4
|
+
Summary: QALITA Platform Command Line Interface
|
|
5
|
+
Author-email: QALITA SAS <contact@qalita.io>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Information Technology
|
|
11
|
+
Classifier: License :: Other/Proprietary License
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Operating System :: Unix
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: azure-storage-blob>=12.19.1
|
|
21
|
+
Requires-Dist: boto3>=1.34.81
|
|
22
|
+
Requires-Dist: click>=8.1.3
|
|
23
|
+
Requires-Dist: croniter>=1.4.1
|
|
24
|
+
Requires-Dist: cryptography>=44.0.1
|
|
25
|
+
Requires-Dist: flask-socketio>=5.3.0
|
|
26
|
+
Requires-Dist: flask>=3.0.3
|
|
27
|
+
Requires-Dist: gevent-websocket>=0.10.1
|
|
28
|
+
Requires-Dist: gevent>=24.2.0
|
|
29
|
+
Requires-Dist: google-cloud-storage>=2.16.0
|
|
30
|
+
Requires-Dist: hdfs>=2.7.3
|
|
31
|
+
Requires-Dist: loguru>=0.7.0
|
|
32
|
+
Requires-Dist: openpyxl>=3.1.5
|
|
33
|
+
Requires-Dist: oracledb>=2.5.0
|
|
34
|
+
Requires-Dist: paramiko>=3.4.0
|
|
35
|
+
Requires-Dist: psycopg2-binary>=2.9.9
|
|
36
|
+
Requires-Dist: pymongo>=4.6.3
|
|
37
|
+
Requires-Dist: pymysql>=1.1.0
|
|
38
|
+
Requires-Dist: python-socketio>=5.10.0
|
|
39
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
40
|
+
Requires-Dist: requests>=2.31.0
|
|
41
|
+
Requires-Dist: semver>=3.0.1
|
|
42
|
+
Requires-Dist: setuptools-rust>=1.8.1
|
|
43
|
+
Requires-Dist: tabulate>=0.9.0
|
|
44
|
+
Requires-Dist: toml>=0.10.2
|
|
45
|
+
Requires-Dist: waitress==3.0.2
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: black>=25.1.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: flake8<4.0,>=3.8.1; extra == 'dev'
|
|
49
|
+
Requires-Dist: pre-commit>=3.3.3; extra == 'dev'
|
|
50
|
+
Requires-Dist: pylint>=2.17.4; extra == 'dev'
|
|
51
|
+
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
52
|
+
Provides-Extra: studio
|
|
53
|
+
Requires-Dist: qalita-studio>=0.1.0; extra == 'studio'
|
|
54
|
+
Description-Content-Type: text/markdown
|
|
55
|
+
|
|
56
|
+
# QALITA Command Line Interface (CLI)
|
|
57
|
+
|
|
58
|
+
<div style="text-align:center;">
|
|
59
|
+
<img width="250px" height="auto" src="https://cloud.platform.qalita.io/logo.svg" style="max-width:250px;"/>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
QALITA Command Line Interface (CLI) is a tool intended to be used by Data Engineers who setup's QALITA Platform's agents, sources and assets.
|
|
63
|
+
|
|
64
|
+
It gives easy to use command to help them make an up & running qalita platform's environment in no time.
|
|
65
|
+
|
|
66
|
+
Find the full documentation [in the online documentation](https://doc.qalita.io/docs/cli)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
qalita/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
qalita/__main__.py,sha256=BIPwbMoOfE3trGx_lMut9L0G0vzrxXFsEwOF4sxCPYI,12654
|
|
3
|
+
qalita/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
qalita/commands/pack.py,sha256=Eu1h-JJEtUBqr6OKPnwE8EuJOQcHpJtZBvKe0XFQaDg,37224
|
|
5
|
+
qalita/commands/source.py,sha256=ObRD2CcDynGbMrmj__FhWvPvR0H6HEn5QSLNR8awQUQ,30945
|
|
6
|
+
qalita/commands/worker.py,sha256=wDIaljpPpvKikbgC1L4IBTVmSradlWkHu0AeKSIrqXw,50642
|
|
7
|
+
qalita/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
qalita/internal/config.py,sha256=FVooOC7T-IXOdzl3Q4TPr9ErhqP03QXmRSUo6OhIqIo,4314
|
|
9
|
+
qalita/internal/error_patterns.py,sha256=QDFNZEoTmqVOHkiGFljstY6VOhux7d4p8FIL3pKzwtQ,1447
|
|
10
|
+
qalita/internal/logger.py,sha256=4xWy2BZII89nUw4tKrLXFPOlVGONm8KUBgzX2XZ3u7g,1868
|
|
11
|
+
qalita/internal/request.py,sha256=s-rE9kKRhV9IEveclB2YjAY8FRevKb6qjblM8-PbV20,6209
|
|
12
|
+
qalita/internal/utils.py,sha256=Ph_OffznffqQn5_JuFz2RmKjtcEIhjAVSLB8CzbacxQ,5746
|
|
13
|
+
qalita/web/__init__.py,sha256=NRcQjZSizkqN0a-LutsN7X9LBQ8MhyazXBSuqzUEmXE,62
|
|
14
|
+
qalita/web/app.py,sha256=UvAzP7HcwE7E48ZEQViHHgVPE4Xw4TcIgdC7D_4xW4A,4973
|
|
15
|
+
qalita/web/blueprints/context.py,sha256=Ji0D3yjDF1VeKsqL8rv8PlNNpLSbxPx7GBKEWnrgnlk,4689
|
|
16
|
+
qalita/web/blueprints/dashboard.py,sha256=Zai5bMn50WWOzWRkkeWzKBnshMOZ7CK5tL7_jnTuXcA,5986
|
|
17
|
+
qalita/web/blueprints/helpers.py,sha256=4lSqT01IaWTFkFQB7J2-R5XKKsc4iDAxzeprPhhQIxI,17788
|
|
18
|
+
qalita/web/blueprints/sources.py,sha256=gjQqQMCkmJEh69Y2EJto-wyVDhk4VV9-9_o115W1O7g,33905
|
|
19
|
+
qalita/web/blueprints/workers.py,sha256=fsjLnJHrPmytxpJIrNaScmlgJ6qvYeMaXlBGYpMvFro,15868
|
|
20
|
+
qalita-2.5.2.dist-info/METADATA,sha256=mavlHXU1320kYk0rL40xZC6OqKek0T7pVqx8wo7RzKo,2524
|
|
21
|
+
qalita-2.5.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
22
|
+
qalita-2.5.2.dist-info/entry_points.txt,sha256=h_4bMcWq-LKrSLca7IAPQnqlZkxz7BBH8eio4nqZCVU,48
|
|
23
|
+
qalita-2.5.2.dist-info/licenses/LICENSE,sha256=cZt92dnxw87-VK4HB6KnmYV7mpf4JUdBkAHzFn1kQxM,22458
|
|
24
|
+
qalita-2.5.2.dist-info/RECORD,,
|