Creating Real-Time Charts with Flask

Update Charts Automatically using Server-Sent Events (SSE) on Flask

Posted 2019-04-30 15:16:49 by Ronie Martinez

Real-time chart example

Creating charts that update automatically is one common use case that web developers commonly implement. With this, HTTP polling is commonly used wherein the client sends a request to the server. Another method is to use WebSockets

Server-Sent Events (SSE)

Server-Sent Events is another way of pushing updates to a client. The difference between WebSockets and SSE is that a WebSocket is two-way while SSE is a one-way communication. SSE is optimum for pushing notifications to the client since there is no requirement of sending back messages to the server in real-time. 

SSE in Flask

To implement SSE in a Flask server, it is only needed to stream data using a generator and set mimetype to text/event-stream.

# generate_random_data() is a generator
return Response(generate_random_data(), mimetype='text/event-stream')

SSE in Browsers (Javascript)

To implement SSE in a browser, instantiate an EventSource and bind a function to the onmessage event.

const source = new EventSource("<URL>");

source.onmessage = function (event) {
   # use event parameter
}

Implementing Real-Time Charts Using SSE

In this example, we will use Flask and Chart.js. The code below shows our Flask server implementation. generate_random_data() yield values from 0 to 100 and the current timestamp.

import json
import random
import time
from datetime import datetime

from flask import Flask, Response, render_template

application = Flask(__name__)
random.seed()  # Initialize the random number generator


@application.route('/')
def index():
    return render_template('index.html')


@application.route('/chart-data')
def chart_data():
    def generate_random_data():
        while True:
            json_data = json.dumps(
                {'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'value': random.random() * 100})
            yield f"data:{json_data}\n\n"
            time.sleep(1)

    return Response(generate_random_data(), mimetype='text/event-stream')


if __name__ == '__main__':
    application.run(debug=True, threaded=True)

The code below shows how easy it is to update charts. We used Twitter Bootstrap to easily manage the page style. First, we instantiate the Chart and EventSource objects. Then on the onmessage event, we parse the JSON data pushed by the server and update our chart array.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Creating Real-Time Charts with Flask</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-12">
            <div class="card">
                <div class="card-body">
                    <canvas id="canvas"></canvas>
                </div>
            </div>
        </div>
    </div>
</div>
<!--suppress JSUnresolvedLibraryURL -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<!--suppress JSUnresolvedLibraryURL -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<!--suppress JSUnresolvedLibraryURL -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
<script>
    $(document).ready(function () {
        const config = {
            type: 'line',
            data: {
                labels: [],
                datasets: [{
                    label: "Random Dataset",
                    backgroundColor: 'rgb(255, 99, 132)',
                    borderColor: 'rgb(255, 99, 132)',
                    data: [],
                    fill: false,
                }],
            },
            options: {
                responsive: true,
                title: {
                    display: true,
                    text: 'Creating Real-Time Charts with Flask'
                },
                tooltips: {
                    mode: 'index',
                    intersect: false,
                },
                hover: {
                    mode: 'nearest',
                    intersect: true
                },
                scales: {
                    xAxes: [{
                        display: true,
                        scaleLabel: {
                            display: true,
                            labelString: 'Time'
                        }
                    }],
                    yAxes: [{
                        display: true,
                        scaleLabel: {
                            display: true,
                            labelString: 'Value'
                        }
                    }]
                }
            }
        };

        const context = document.getElementById('canvas').getContext('2d');

        const lineChart = new Chart(context, config);

        const source = new EventSource("/chart-data");

        source.onmessage = function (event) {
            const data = JSON.parse(event.data);
            if (config.data.labels.length === 20) {
                config.data.labels.shift();
                config.data.datasets[0].data.shift();
            }
            config.data.labels.push(data.time);
            config.data.datasets[0].data.push(data.value);
            lineChart.update();
        }
    });
</script>
</body>
</html>

What's next?

This approach can also be implemented using other popular client-side charting libraries. Handling multiple charts at the same time, should be pretty straightforward.

Sample application is available on Github.

References

server-sent events python flask chart.js


Share