Blog der Heimetli Software AG

Google Linechart mit Daten von Flask

Im allgemeinen werden die Daten für Google Charts mit AJAX in die HTML-Seite geladen. Wenn man das nicht will, kann man es auch mit Flask realisieren.

Die Flask-App

from flask import Flask, render_template, request
import random
import datetime

app = Flask( __name__ )

class Observation:
    def __init__( self, timestamp, value ):
        self.timestamp = timestamp
        self.value     = value
        self.tooltip   = f"{timestamp.strftime('%H:%M')} {value:.1f}"

def generate_observations( hours ):
    timestamp = datetime.datetime.now() - datetime.timedelta( minutes=hours*60 )
    value     = 20
    result    = []

    for i in range(hours*60):
        value     += random.random() * 5 - 2.4
        timestamp += datetime.timedelta( minutes=1 )
        result.append( Observation(timestamp,value) )

    return result


@app.route( "/", methods=("GET","POST") )
def index():
    hours = 1

    if request.method == "POST":
        hours = int(request.form.get( "hours","1"))

    return render_template( "index.html", observations=generate_observations(hours), hours=hours )

Um den Code einfach zu halten sind die Daten frei erfunden. Für eine reale Applikation müsste generate_observations durch eine Funktion ersetzt werden die die echten Daten liefert.

Die Route akzeptiert sowohl GET als auch POST als Methode. Bei GET wird die Seite mit einem Default von einer Stunde ausgeliefert. Beim POST wird ein Parameter mitgeliefert der die Anzahl der Stunden bestimmt.

Das Template

<!DOCTYPE html>
<html lang="de">
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="Temperatur mit Google Charts darstellen">
  <title>Google Chart parametriert durch flask</title>
  <style>
   div#temperature { height: 20em; }
   form            { margin-bottom: 2em; }
  </style>
  <script src="https://www.gstatic.com/charts/loader.js"></script>
 </head>
 <body>
  <h1>Temperaturverlauf</h1>
  <form action="/" method="post">
   <label for="sel">Stunden</label>
   <select name="hours" id="sel">
    <option value="1"{% if hours == 1 %} selected{% endif %}>1</option>
    <option value="2"{% if hours == 2 %} selected{% endif %}>2</option>
    <option value="3"{% if hours == 3 %} selected{% endif %}>3</option>
    <option value="4"{% if hours == 4 %} selected{% endif %}>4</option>
    <option value="5"{% if hours == 5 %} selected{% endif %}>5</option>
   </select>
   <input type="submit" value="Anzeigen">
  </form>
  <div id="temperature"></div>
  <script>
    function drawChart()
    {
       let data = new google.visualization.DataTable() ;
       data.addColumn('datetime', 'time') ;
       data.addColumn('number', 'Temperatur') ;
       data.addColumn({type: 'string', role: 'tooltip'}) ;
      
       data.addRows([
          {% for o in observations -%}
            [new Date({{o.timestamp.year}},{{o.timestamp.month-1}},{{o.timestamp.day}},{{o.timestamp.hour}},{{o.timestamp.minute}},0), {{ o.value }}, "{{ o.tooltip }}"]{%- if not loop.last -%},{% endif %}
          {% endfor %}
       ]);

       let options = {
           explorer: {
             axis: "horizontal"
           },
           chartArea: { left: "10%", top: "10%", width: "85%", height: "80%" },
           legend: "none",
           hAxis: {
             format: "kk:mm"
           },
           vAxis: {
             title: "Temperatur"
           },
           backgroundColor: "#f1f8e9"
       } ;

       let chart = new google.visualization.LineChart( document.getElementById('temperature') ) ;

       chart.draw( data, options ) ;
    }

    google.charts.load( 'current', {'packages':['corechart','line']} ) ;
    google.charts.setOnLoadCallback( drawChart ) ;
  </script>
 </body>
</html>

Der Code für den Chart wird direkt von einem Google-Server geladen. Die Daten für den Chart werden im Template eingesetzt.

Setup

app.py kann in einem beliebigen Directory liegen. Wichtig ist nur dass es darunter ein Directory namens templates gibt. Das oben gezeigte Template muss dort als index.html abgelegt sein.

Start

Auf der Kommandozeile diese beiden Kommandos eingeben:

export FLASK_APP=app.py
flask run --host=0.0.0.0

Per Default nimmt Flask das Port 5000. Der Graph ist also zum Beispiel über localhost:5000 zu erreichen.