[FIXED] How can I access a dynamically updating JSON variable from Python to JavaScript?

Issue

I am creating a live chart that dynamically updates itself. I am taking a tuple of (current time, random integer) for my chart from a dummy database, sender.py. Here is the code for sender.py

import time
import random
import json
from datetime import datetime


def get_coordinates():
    while True:
        json_data = json.dumps(
            {
                'time' : datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 
                'value' : random.random() * 1000
            })
        yield "data:{}\n\n".format(json_data)
        time.sleep(1)

I share this data to the route, /chart-data, made in app.py

import os

from flask import Flask, render_template, Response

from sender import get_coordinates

app = Flask(__name__)


@app.route('/chart-data', methods = ['POST'])
def chart_data():
    get_coordinates()
    return Response(get_coordinates())


@app.route("/")
def index():
    return render_template('data.html')

I upload json_data from sender.py to the route /chart-data and want to get this in data.html. Here is the .html file (I am trying to retrieve this data in $.ajax() method ) –

...
<body>

    <canvas id = 'myChart' width = "900" height = "400"></canvas>
    <script>
        
        var intervalID = setInterval(update_values, 1000);
        var x_axis = 0;
        var y_axis;
        
        function update_values() {
            $.ajax({
                 url: '/data',
                 type: 'GET',
                 success: function(result) {
                     //want to get x-axis and y-axis from sender.py
                 },
      
            })

            x_axis = x_axis + 1;

            myChart.data.labels.push(x_axis);
            myChart.data.datasets.forEach((datasets) => {
                datasets.data.push(y_axis);
            });

            myChart.update();
        };

        ...
    </script>

</body>

Solution

Using a generator to stream data continuously does not make sense in this context. A possible solution is to get the current data with regular AJAX requests.
The following simple example shows you how to create a dynamically updating chart.

The application consists of two traditional endpoints. The former serves the actual page while the latter provides the data. With each request, additional data is generated and delivered in JSON format.

from flask import (
    Flask,
    jsonify,
    render_template
)
from datetime import datetime
import random

app = Flask(__name__)

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

@app.route('/data')
def data():
    return jsonify({
        'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        'value': random.randint(0, 1000)
    })

Within the page, data is obtained from the server at regular intervals using the Fetch API. The data obtained is added to the chart. If there are already more than 10 data records, the oldest ones will be removed.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Index</title>
  </head>
  <body>
    <div>
      <canvas id="myChart" width="900" height="400"></canvas>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script type="text/javascript">
      (function(url) {
        const limit = 9;
        const canvas = document.getElementById('myChart');
        const chart = new Chart(canvas, {
          type: 'line',
          data: {
            labels: [],
            datasets: [{
              label: 'MyDataset',
              data: []
            }]
          },
          options: {
            scales: {
              yAxis: {
                max: 1000,
                min: 0
              }
            }
          }
        });

        setInterval(async () => {
          const data = await fetch(url).then(resp => resp.ok && resp.json());
          if (data) {
            const { time, value } = data;

            const overflow = chart.data.labels.length - limit;
            if (overflow > 0) {
              chart.data.labels.splice(0, overflow);
              chart.data.datasets[0].data.splice(0, overflow);
            }

            chart.data.labels.push(time);
            chart.data.datasets[0].data.push(value);
            chart.update();
          }
        }, 1000);

      })({{ url_for('data') | tojson }});
    </script>
  </body>
</html>

Answered By – Detlef

Answer Checked By – Cary Denson (Easybugfix Admin)

Leave a Reply

(*) Required, Your email will not be published