0

Overall, my goal is to convert several console-based Python scripts into Flask, so they can be deployed as webapps.

Current challenge: converting a series of console print() messages into a streaming update in the HTML page.

It seems like these messages could be appended to a single string array, that is returned and shown on index.html. But there needs to be a way to trigger the HTML to update from the server side, since the user isn't going to supply any input.

I cobbled together some scripts from Streaming data with Python and Flask and Display data streamed from a Flask view as it updates. It doesn't show the messages, though, just "nothing received yet."

app.py:

from flask import Flask, Response, jsonify, render_template, request,    redirect, url_for

import time
from time import sleep

app = Flask(__name__)

@app.route('/')
def index():
    if request.headers.get('accept') == 'text/event-stream':
       messages = []
       def script():
           #a lot of code goes here
           yield "data: Part A completed.\n\n"

           #more code
           sleep(10)
           yield "data: Part B completed.\n\n"

           #more code
           sleep(10)
           yield "data: Part C completed.\n\n"

        return Response(script(), content_type='text/event-stream')
    return redirect(url_for('static', filename='index.html'))

if __name__ == '__main__':
    app.run()

index.html:

<!doctype html>
<title>Messages</title>
<style>
    #messages {
        text-align: left;
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
if (!!window.EventSource) {
  var source = new EventSource('/');
  source.onmessage = function(e) {
    $("#messages").text(e.data);
  }
}
</script>
<div id="messages">nothing received yet</div>

UPDATED to include Sergey's notes below

Community
  • 1
  • 1
Suzanne
  • 582
  • 2
  • 11
  • 31

1 Answers1

1

The point of error is that you try to send list of messages instead of text. You don't need to store messages in a list, you can just yield one message a time. Also the message consists of several fields and 'data' field is required:

@app.route('/')
def index():
    if request.headers.get('accept') == 'text/event-stream':
        def script():
            #a lot of code goes here
            yield "data: Part A completed.\n\n"

            #more code
            sleep(10)
            yield "data: Part B completed.\n\n"

            #more code
            sleep(10)
            yield "data: Part C completed.\n\n"

        return Response(script(), content_type='text/event-stream')
    return redirect(url_for('static', filename='index.html'))

To add messages to HTML page it should look like this:

<html>
  <head>
    <title>Messages</title>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script>
    if (!!window.EventSource) {
        var source = new EventSource('/');
        source.onmessage = function(e) {
            var m = $("#messages");
            if (m.text() == 'nothing received yet')
                m.html("<div>" + e.data + "</div>");
            else
                m.append("<div>" + e.data + "</div>");
        }
    }
    </script>
  </head>
  <body>
    <div id="messages">nothing received yet</div>
  </body>
</html>
Sergey Shubin
  • 3,040
  • 4
  • 24
  • 36
  • This also gives "nothing received yet." – Suzanne Jan 16 '17 at 13:41
  • @Suzanne Found a couple more issues — on server-side and client-side, updated my answer accordingly. – Sergey Shubin Jan 16 '17 at 14:06
  • OK. I have edited my question to show what I'm using now. It still shows "nothing received yet." – Suzanne Jan 16 '17 at 18:15
  • 1
    @Suzanne Strange, your code now works on my machine. Could you launch this code as is (it will just need some indentation fixing and it's ready)? index.html also doesn't look like valid HTML code but Firefox and Chrome browsers deal with it just fine. – Sergey Shubin Jan 17 '17 at 07:46
  • Thank you - I tried this on a different machine, and it works as expected! How can it be tweaked so that the messages appear below each other, rather than replacing each other? (What I tried originally.) `messages.append("Part A completed.\n\n")` `yield "data: %s" % (messages)` again results in a static "no data yet." – Suzanne Jan 17 '17 at 14:29
  • 1
    @Suzanne You need to fix it on client side: replace `$("#messages").text(e.data);` with `var m = $("#messages"); if (m.text() == "nothing received yet") m.html("
    " + e.data + "
    "); else m.append("
    " + e.data + "
    ");`. It will add new messages to `div` block. [More information on HTML manipulation functions](http://api.jquery.com/category/manipulation/)
    – Sergey Shubin Jan 18 '17 at 07:58