[Feature] Async Prediction Status Poll (#93)

Fixed #81

Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: enviPath/enviPy#93
This commit is contained in:
2025-09-09 20:39:26 +12:00
parent 5477b5b3d4
commit 3453a169e1
3 changed files with 68 additions and 8 deletions

View File

@ -1358,14 +1358,17 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
return self.kv.get('mode', 'build') == 'predicted'
# Status
def status(self):
return self.kv.get('status', 'completed')
def completed(self):
return self.kv.get('status', 'completed') == 'completed'
return self.status() == 'completed'
def running(self):
return self.kv.get('status', 'completed') == 'running'
return self.status() == 'running'
def failed(self):
return self.kv.get('status', 'completed') == 'failed'
return self.status() == 'failed'
def d3_json(self):
# Ideally it would be something like this but
@ -1472,7 +1475,8 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
"upToDate": True,
"links": adjusted_links,
"nodes": nodes,
"modified": self.modified.strftime('%Y-%m-%d %H:%M:%S')
"modified": self.modified.strftime('%Y-%m-%d %H:%M:%S'),
"status": self.status(),
}
return json.dumps(res)

View File

@ -1438,6 +1438,12 @@ def package_pathway(request, package_uuid, pathway_uuid):
if request.GET.get("last_modified", False):
return JsonResponse({'modified': current_pathway.modified.strftime('%Y-%m-%d %H:%M:%S')})
if request.GET.get('status', False):
return JsonResponse({
'status': current_pathway.status(),
'modified': current_pathway.modified.strftime('%Y-%m-%d %H:%M:%S'),
})
if request.GET.get("download", False) == "true":
filename = f"{current_pathway.name.replace(' ', '_')}_{current_pathway.uuid}.csv"
csv_pw = current_pathway.to_csv()

View File

@ -149,19 +149,19 @@
</li>
<li>
{% if pathway.completed %}
<button type="button" class="btn btn-default navbar-btn" data-toggle="tooltip"
<button type="button" class="btn btn-default navbar-btn" data-toggle="popover"
id="status" data-original-title="" title=""
data-content="Pathway prediction complete.">
<span class="glyphicon glyphicon-ok"></span>
</button>
{% elif pathway.failed %}
<button type="button" class="btn btn-default navbar-btn" data-toggle="tooltip"
<button type="button" class="btn btn-default navbar-btn" data-toggle="popover"
id="status" data-original-title="" title=""
data-content="Pathway prediction failed.">
<span class="glyphicon glyphicon-remove"></span>
</button>
{% else %}
<button type="button" class="btn btn-default navbar-btn" data-toggle="tooltip"
<button type="button" class="btn btn-default navbar-btn" data-toggle="popover"
id="status" data-original-title="" title=""
data-content="Pathway prediction running.">
<img height="20" src="{% static '/images/wait.gif' %}">
@ -317,7 +317,7 @@
</div>
</div>
<script>
// Globla switch for app domain view
// Global switch for app domain view
var appDomainViewEnabled = false;
function goFullscreen(id) {
@ -336,6 +336,56 @@
pathway = {{ pathway.d3_json | safe }};
$(function () {
$('#status').popover({
trigger: 'manual',
placement: 'bottom',
html: true
});
// If prediction is still running, regularly check status
if (pathway.status === 'running') {
let last_modified = pathway.modified;
let pollInterval = setInterval(async () => {
try {
const response = await fetch("{{ pathway.url }}?status=true", {});
const data = await response.json();
if (data.modified > last_modified) {
var msg = 'Prediction '
var btn = '<button type="button" onclick="location.reload()" class="btn btn-primary" id="reloadBtn">Reload page</button>'
if (data.status === "running") {
msg += 'is still running. But the Pathway was updated.<br>' + btn;
} else if (data.status === "completed") {
msg += 'is completed. Reload the page to see the updated Pathway.<br>' + btn;
} else if (data.status === "failed") {
msg += 'failed. Reload the page to see the current shape<br>' + btn;
}
$('#status').attr(
'data-content', msg
).popover('show');
}
if (data.status === "completed" || data.status === "failed") {
$('#status img').remove();
if (data.status === "completed") {
$('#status').append('<span class="glyphicon glyphicon-ok"></span>')
} else {
$('#status').append('<span class="glyphicon glyphicon-remove"></span>')
}
clearInterval(pollInterval);
}
} catch (err) {
console.error("Polling error:", err);
}
}, 5000);
}
draw(pathway, 'vizdiv');
// TODO fix somewhere else...
var newDesc = transformReferences($('#DescriptionContent')[0].innerText);