How can one redirect to another page when server-sent-event is finished in Flask and on server side?
An EventSource was implemented on client side code (JavaScript) and a Response was returned on server side code (Flask, Python). The EventSource will be closed if the last item from server was sent. I can't see a clear solution to redirect to another site via server side code. Maybe the answer is simple, but I'm not getting it.
A similar question is How to stop Server-Sent Events and one possible answer in the comment How do server-sent events actually work?. I'm not sure, if it's the same for php and python, so I've started a new question for that. I also didn't get it.
EDIT How do I close a Server-Send Events connection in Flask? is strongly related to this question. The question there is mainly to see how you can stop SSE from Flask.
So I came up with a solution that worked (a progress bar), but only on the client side. How do I have to change the code to get a working example of reconnecting via Flask functions?
HTML/Jinja
{% block additional_stylesheets %}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='data_import.css') }}" />
{% endblock %}
{% block additional_javascripts %}
<script src="{{ url_for('static', filename='js/data_import.js') }}"></script>
{% endblock %}
{% block content %}
<div class="progress">
<div class="progress-bar progress-bar-striped active"
role="progressbar" aria-valuenow="0" aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
{% endblock %}
JavaScript
$(document).ready(function()
{
var source = new EventSource("/progress");
source.addEventListener('import-progress', function(event)
{
$('.progress-bar')
.css('width', event.data + '%')
.attr('aria-valuenow', event.data);
}, false
);
source.addEventListener('last-item', function()
{
source.close();
redirect();
}, false
);
}
);
# This works! But how to do the same thing without calling redirect()
# on client site?
function redirect()
{
window.document.location.href = window.location.protocol + "//" +
window.location.host + "/products.html";
}
Flask
from foo import app
from flask import render_template, Response
@app.route('/data_import.html')
def data_import():
return render_template(
'data_import.html')
@app.route('/progress')
def progress():
return Response(import_progress(), mimetype='text/event-stream')
def import_progress():
"""
Just a small example
"""
for number in range(1, 101):
sse_id = str(number)
sse_progress = str(number)
sse_event = 'import-progress'
if number == 100:
sse_event = 'last-item'
yield "id:{_id}\nevent:{event}\ndata:{progress}\n\n".format(
_id=sse_id, event=sse_event, progress=sse_progress)
I tried a lot to get a redirect working. But I don't know exactly how to do it. Every attempt so far has failed.
What you want to do is send the redirect URL as the last event - you'll still need to redirect using JavaScript, but you won't have to hard-code the path any more:
def import_progress():
"""
Just a small example
"""
for number in range(1, 101):
sse_id = str(number)
sse_data = str(number)
sse_event = 'import-progress'
if number == 100:
sse_event = 'last-item'
sse_data = url_for('product_list')
yield "id:{_id}\nevent:{event}\ndata:{data}\n\n".format(
_id=sse_id, event=sse_event, data=sse_data)
Then your last-item handler becomes:
source.addEventListener('last-item', function(event) {
source.close();
redirect(event.data);
}, false
);
And redirect
becomes a simple:
function redirect(url) {
document.location = url;
}