Spaces:
Running
Running
Commit ·
5083f77
1
Parent(s): 39be855
Show search queue position and activity
Browse files
application_neo4j/app.py
CHANGED
|
@@ -287,6 +287,11 @@ def inject_i18n():
|
|
| 287 |
"search.progress_stage_completed", "search.progress_stage_failed",
|
| 288 |
"search.progress_stage_cancelled",
|
| 289 |
"search.progress_elapsed", "search.progress_items",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
"search.progress_server_percent",
|
| 291 |
"search.progress_remaining_step", "search.progress_server_note",
|
| 292 |
"search.progress_connection_error",
|
|
@@ -711,6 +716,25 @@ def search_job_status(job_id):
|
|
| 711 |
with search_jobs_lock:
|
| 712 |
stored_job = search_jobs.get(job_id)
|
| 713 |
job = dict(stored_job) if stored_job else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 714 |
if not job:
|
| 715 |
return jsonify({"error": "Search job not found"}), 404
|
| 716 |
|
|
@@ -722,6 +746,14 @@ def search_job_status(job_id):
|
|
| 722 |
"elapsed_seconds": max(0, math.floor(now - started_at)) if started_at else 0,
|
| 723 |
"progress_percent": None,
|
| 724 |
"remaining_seconds": None,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 725 |
"completed_items": job.get("completed_items"),
|
| 726 |
"total_items": job.get("total_items"),
|
| 727 |
"result_url": (
|
|
|
|
| 287 |
"search.progress_stage_completed", "search.progress_stage_failed",
|
| 288 |
"search.progress_stage_cancelled",
|
| 289 |
"search.progress_elapsed", "search.progress_items",
|
| 290 |
+
"search.progress_queue_title", "search.progress_queue_wait",
|
| 291 |
+
"search.progress_queue_position", "search.progress_queue_ahead_one",
|
| 292 |
+
"search.progress_queue_ahead_many", "search.progress_queue_next",
|
| 293 |
+
"search.progress_queue_running_one", "search.progress_queue_running_many",
|
| 294 |
+
"search.progress_queue_note",
|
| 295 |
"search.progress_server_percent",
|
| 296 |
"search.progress_remaining_step", "search.progress_server_note",
|
| 297 |
"search.progress_connection_error",
|
|
|
|
| 716 |
with search_jobs_lock:
|
| 717 |
stored_job = search_jobs.get(job_id)
|
| 718 |
job = dict(stored_job) if stored_job else None
|
| 719 |
+
queued_jobs = sorted(
|
| 720 |
+
(
|
| 721 |
+
(queued_job.get("created_at", 0), queued_job_id)
|
| 722 |
+
for queued_job_id, queued_job in search_jobs.items()
|
| 723 |
+
if queued_job.get("status") == "queued"
|
| 724 |
+
)
|
| 725 |
+
)
|
| 726 |
+
queued_job_ids = [
|
| 727 |
+
queued_job_id for _, queued_job_id in queued_jobs
|
| 728 |
+
]
|
| 729 |
+
queue_position = (
|
| 730 |
+
queued_job_ids.index(job_id) + 1
|
| 731 |
+
if job_id in queued_job_ids
|
| 732 |
+
else None
|
| 733 |
+
)
|
| 734 |
+
running_jobs = sum(
|
| 735 |
+
queued_job.get("status") == "running"
|
| 736 |
+
for queued_job in search_jobs.values()
|
| 737 |
+
)
|
| 738 |
if not job:
|
| 739 |
return jsonify({"error": "Search job not found"}), 404
|
| 740 |
|
|
|
|
| 746 |
"elapsed_seconds": max(0, math.floor(now - started_at)) if started_at else 0,
|
| 747 |
"progress_percent": None,
|
| 748 |
"remaining_seconds": None,
|
| 749 |
+
"queue_position": queue_position,
|
| 750 |
+
"queued_jobs": len(queued_job_ids),
|
| 751 |
+
"running_jobs": running_jobs,
|
| 752 |
+
"queue_wait_seconds": (
|
| 753 |
+
max(0, math.floor(now - job.get("created_at", now)))
|
| 754 |
+
if job["status"] == "queued"
|
| 755 |
+
else None
|
| 756 |
+
),
|
| 757 |
"completed_items": job.get("completed_items"),
|
| 758 |
"total_items": job.get("total_items"),
|
| 759 |
"result_url": (
|
application_neo4j/static/js/search_progress.js
CHANGED
|
@@ -95,6 +95,7 @@
|
|
| 95 |
}
|
| 96 |
|
| 97 |
function renderStatus(progress, job) {
|
|
|
|
| 98 |
const stage = progress.querySelector(".search-progress-stage");
|
| 99 |
const status = progress.querySelector(".search-progress-status");
|
| 100 |
const note = progress.querySelector(".search-progress-note");
|
|
@@ -102,6 +103,92 @@
|
|
| 102 |
|
| 103 |
stage.textContent = stageLabel(job.stage);
|
| 104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
const details = [
|
| 106 |
format(
|
| 107 |
translate("search.progress_elapsed", "Temps écoulé : {seconds} s"),
|
|
@@ -208,6 +295,10 @@
|
|
| 208 |
elapsed_seconds: 0,
|
| 209 |
progress_percent: null,
|
| 210 |
remaining_seconds: null,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
completed_items: null,
|
| 212 |
total_items: null,
|
| 213 |
});
|
|
|
|
| 95 |
}
|
| 96 |
|
| 97 |
function renderStatus(progress, job) {
|
| 98 |
+
const title = progress.querySelector(".search-progress-title");
|
| 99 |
const stage = progress.querySelector(".search-progress-stage");
|
| 100 |
const status = progress.querySelector(".search-progress-status");
|
| 101 |
const note = progress.querySelector(".search-progress-note");
|
|
|
|
| 103 |
|
| 104 |
stage.textContent = stageLabel(job.stage);
|
| 105 |
|
| 106 |
+
if (job.status === "queued" || job.stage === "queued") {
|
| 107 |
+
title.textContent = translate(
|
| 108 |
+
"search.progress_queue_title",
|
| 109 |
+
"Recherche en file d’attente"
|
| 110 |
+
);
|
| 111 |
+
progress.classList.remove("alert-info", "alert-danger", "alert-secondary");
|
| 112 |
+
progress.classList.add("alert-warning");
|
| 113 |
+
|
| 114 |
+
const queueDetails = [
|
| 115 |
+
format(
|
| 116 |
+
translate(
|
| 117 |
+
"search.progress_queue_wait",
|
| 118 |
+
"Temps d’attente : {seconds} s"
|
| 119 |
+
),
|
| 120 |
+
{ seconds: job.queue_wait_seconds ?? 0 }
|
| 121 |
+
),
|
| 122 |
+
];
|
| 123 |
+
if (job.queue_position && job.queued_jobs) {
|
| 124 |
+
queueDetails.push(
|
| 125 |
+
format(
|
| 126 |
+
translate(
|
| 127 |
+
"search.progress_queue_position",
|
| 128 |
+
"Position dans la file : {position}/{total}"
|
| 129 |
+
),
|
| 130 |
+
{
|
| 131 |
+
position: job.queue_position,
|
| 132 |
+
total: job.queued_jobs,
|
| 133 |
+
}
|
| 134 |
+
)
|
| 135 |
+
);
|
| 136 |
+
const jobsAhead = job.queue_position - 1;
|
| 137 |
+
if (jobsAhead === 0) {
|
| 138 |
+
queueDetails.push(
|
| 139 |
+
translate(
|
| 140 |
+
"search.progress_queue_next",
|
| 141 |
+
"Votre recherche est la prochaine."
|
| 142 |
+
)
|
| 143 |
+
);
|
| 144 |
+
} else {
|
| 145 |
+
queueDetails.push(
|
| 146 |
+
format(
|
| 147 |
+
translate(
|
| 148 |
+
jobsAhead === 1
|
| 149 |
+
? "search.progress_queue_ahead_one"
|
| 150 |
+
: "search.progress_queue_ahead_many",
|
| 151 |
+
jobsAhead === 1
|
| 152 |
+
? "1 recherche devant la vôtre"
|
| 153 |
+
: "{count} recherches devant la vôtre"
|
| 154 |
+
),
|
| 155 |
+
{ count: jobsAhead }
|
| 156 |
+
)
|
| 157 |
+
);
|
| 158 |
+
}
|
| 159 |
+
}
|
| 160 |
+
if (job.running_jobs) {
|
| 161 |
+
queueDetails.push(
|
| 162 |
+
format(
|
| 163 |
+
translate(
|
| 164 |
+
job.running_jobs === 1
|
| 165 |
+
? "search.progress_queue_running_one"
|
| 166 |
+
: "search.progress_queue_running_many",
|
| 167 |
+
job.running_jobs === 1
|
| 168 |
+
? "1 recherche en cours"
|
| 169 |
+
: "{count} recherches en cours"
|
| 170 |
+
),
|
| 171 |
+
{ count: job.running_jobs }
|
| 172 |
+
)
|
| 173 |
+
);
|
| 174 |
+
}
|
| 175 |
+
status.textContent = queueDetails.join(" · ");
|
| 176 |
+
note.textContent = translate(
|
| 177 |
+
"search.progress_queue_note",
|
| 178 |
+
"La recherche démarrera automatiquement dès qu’une capacité sera disponible."
|
| 179 |
+
);
|
| 180 |
+
bar.style.width = "0%";
|
| 181 |
+
bar.removeAttribute("aria-valuenow");
|
| 182 |
+
return;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
title.textContent = translate(
|
| 186 |
+
"search.progress_title",
|
| 187 |
+
"Recherche en cours…"
|
| 188 |
+
);
|
| 189 |
+
progress.classList.remove("alert-warning", "alert-danger", "alert-secondary");
|
| 190 |
+
progress.classList.add("alert-info");
|
| 191 |
+
|
| 192 |
const details = [
|
| 193 |
format(
|
| 194 |
translate("search.progress_elapsed", "Temps écoulé : {seconds} s"),
|
|
|
|
| 295 |
elapsed_seconds: 0,
|
| 296 |
progress_percent: null,
|
| 297 |
remaining_seconds: null,
|
| 298 |
+
queue_position: null,
|
| 299 |
+
queued_jobs: null,
|
| 300 |
+
running_jobs: null,
|
| 301 |
+
queue_wait_seconds: 0,
|
| 302 |
completed_items: null,
|
| 303 |
total_items: null,
|
| 304 |
});
|
application_neo4j/translations.py
CHANGED
|
@@ -108,7 +108,7 @@ TRANSLATIONS = {
|
|
| 108 |
"search.btn_search": "Rechercher",
|
| 109 |
"search.btn_search_simple": "Rechercher",
|
| 110 |
"search.progress_title": "Recherche en cours…",
|
| 111 |
-
"search.progress_stage_queued": "En attente
|
| 112 |
"search.progress_stage_preparing": "Préparation du graphe…",
|
| 113 |
"search.progress_stage_descendants": "Recherche des descendants…",
|
| 114 |
"search.progress_stage_ancestors": "Recherche des ascendants…",
|
|
@@ -122,6 +122,15 @@ TRANSLATIONS = {
|
|
| 122 |
"search.progress_stage_cancelled": "Recherche annulée.",
|
| 123 |
"search.progress_elapsed": "Temps écoulé : {seconds} s",
|
| 124 |
"search.progress_items": "Éléments traités par le serveur : {completed}/{total}",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
"search.progress_server_percent": "Progression indiquée par le serveur : {percent} %",
|
| 126 |
"search.progress_remaining_step": "Temps restant estimé pour cette étape : {seconds} s",
|
| 127 |
"search.progress_server_note": "La progression repose sur les opérations réellement terminées par le serveur.",
|
|
@@ -337,7 +346,7 @@ TRANSLATIONS = {
|
|
| 337 |
"search.btn_search": "Search",
|
| 338 |
"search.btn_search_simple": "Search",
|
| 339 |
"search.progress_title": "Search in progress…",
|
| 340 |
-
"search.progress_stage_queued": "Waiting for
|
| 341 |
"search.progress_stage_preparing": "Preparing the graph…",
|
| 342 |
"search.progress_stage_descendants": "Searching descendants…",
|
| 343 |
"search.progress_stage_ancestors": "Searching ancestors…",
|
|
@@ -351,6 +360,15 @@ TRANSLATIONS = {
|
|
| 351 |
"search.progress_stage_cancelled": "Search cancelled.",
|
| 352 |
"search.progress_elapsed": "Elapsed time: {seconds} s",
|
| 353 |
"search.progress_items": "Items processed by the server: {completed}/{total}",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
"search.progress_server_percent": "Progress reported by the server: {percent}%",
|
| 355 |
"search.progress_remaining_step": "Estimated time remaining for this step: {seconds} s",
|
| 356 |
"search.progress_server_note": "Progress is based on operations actually completed by the server.",
|
|
|
|
| 108 |
"search.btn_search": "Rechercher",
|
| 109 |
"search.btn_search_simple": "Rechercher",
|
| 110 |
"search.progress_title": "Recherche en cours…",
|
| 111 |
+
"search.progress_stage_queued": "En attente d’une capacité de recherche…",
|
| 112 |
"search.progress_stage_preparing": "Préparation du graphe…",
|
| 113 |
"search.progress_stage_descendants": "Recherche des descendants…",
|
| 114 |
"search.progress_stage_ancestors": "Recherche des ascendants…",
|
|
|
|
| 122 |
"search.progress_stage_cancelled": "Recherche annulée.",
|
| 123 |
"search.progress_elapsed": "Temps écoulé : {seconds} s",
|
| 124 |
"search.progress_items": "Éléments traités par le serveur : {completed}/{total}",
|
| 125 |
+
"search.progress_queue_title": "Recherche en file d’attente",
|
| 126 |
+
"search.progress_queue_wait": "Temps d’attente : {seconds} s",
|
| 127 |
+
"search.progress_queue_position": "Position dans la file : {position}/{total}",
|
| 128 |
+
"search.progress_queue_ahead_one": "1 recherche devant la vôtre",
|
| 129 |
+
"search.progress_queue_ahead_many": "{count} recherches devant la vôtre",
|
| 130 |
+
"search.progress_queue_next": "Votre recherche est la prochaine.",
|
| 131 |
+
"search.progress_queue_running_one": "1 recherche en cours",
|
| 132 |
+
"search.progress_queue_running_many": "{count} recherches en cours",
|
| 133 |
+
"search.progress_queue_note": "La recherche démarrera automatiquement dès qu’une capacité sera disponible.",
|
| 134 |
"search.progress_server_percent": "Progression indiquée par le serveur : {percent} %",
|
| 135 |
"search.progress_remaining_step": "Temps restant estimé pour cette étape : {seconds} s",
|
| 136 |
"search.progress_server_note": "La progression repose sur les opérations réellement terminées par le serveur.",
|
|
|
|
| 346 |
"search.btn_search": "Search",
|
| 347 |
"search.btn_search_simple": "Search",
|
| 348 |
"search.progress_title": "Search in progress…",
|
| 349 |
+
"search.progress_stage_queued": "Waiting for search capacity…",
|
| 350 |
"search.progress_stage_preparing": "Preparing the graph…",
|
| 351 |
"search.progress_stage_descendants": "Searching descendants…",
|
| 352 |
"search.progress_stage_ancestors": "Searching ancestors…",
|
|
|
|
| 360 |
"search.progress_stage_cancelled": "Search cancelled.",
|
| 361 |
"search.progress_elapsed": "Elapsed time: {seconds} s",
|
| 362 |
"search.progress_items": "Items processed by the server: {completed}/{total}",
|
| 363 |
+
"search.progress_queue_title": "Search queued",
|
| 364 |
+
"search.progress_queue_wait": "Waiting time: {seconds} s",
|
| 365 |
+
"search.progress_queue_position": "Queue position: {position}/{total}",
|
| 366 |
+
"search.progress_queue_ahead_one": "1 search ahead of yours",
|
| 367 |
+
"search.progress_queue_ahead_many": "{count} searches ahead of yours",
|
| 368 |
+
"search.progress_queue_next": "Your search is next.",
|
| 369 |
+
"search.progress_queue_running_one": "1 search currently running",
|
| 370 |
+
"search.progress_queue_running_many": "{count} searches currently running",
|
| 371 |
+
"search.progress_queue_note": "Your search will start automatically as soon as capacity becomes available.",
|
| 372 |
"search.progress_server_percent": "Progress reported by the server: {percent}%",
|
| 373 |
"search.progress_remaining_step": "Estimated time remaining for this step: {seconds} s",
|
| 374 |
"search.progress_server_note": "Progress is based on operations actually completed by the server.",
|