pydata

Keep Looking, Don't Settle

highcharts in python flask

1. pandas-highcharts

pandas-highcharts is very easy to use. It has some demos showing how to plot. The only con I found is I did not get how to draw dynamic graph through it.

The following example is a small modification of the demo code.

%load_ext autoreload
%autoreload 2

import pandas as pd
import datetime
import os
import numpy as np
from pandas.compat import StringIO
from pandas.io.common import urlopen
from IPython.display import display, display_pretty, Javascript, HTML
from pandas_highcharts.core import serialize
from pandas_highcharts.display import display_charts
import matplotlib.pyplot as plt

1.1 spline plot of two series

import time
import random

s = []
i = time.time()
while i < time.time() + 100:
    i += 1
    s.append([i, random.randint(0, 10), random.random()])
df = pd.DataFrame(s , columns = ["dt", "val1", "val2"])

df.dt = df.dt.astype("datetime64[s]")

print df.head()

df = df.set_index("dt")
                   dt  val1      val2
0 2016-08-18 02:05:47     5  0.139195
1 2016-08-18 02:05:48     9  0.419186
2 2016-08-18 02:05:49     1  0.381455
3 2016-08-18 02:05:50     6  0.275681
4 2016-08-18 02:05:51     9  0.140637
display_charts(df, title="Plot with pandas-highcharts")

Graph 3. 打印出数据(string)

alt text

1.2 cut to count frequency

pd.cut(range(100), 10, right = False, retbins = True)
([[0, 9.9), [0, 9.9), [0, 9.9), [0, 9.9), [0, 9.9), ..., [89.1, 99.099), [89.1, 99.099), [89.1, 99.099), [89.1, 99.099), [89.1, 99.099)]
 Length: 100
 Categories (10, object): [[0, 9.9) < [9.9, 19.8) < [19.8, 29.7) < [29.7, 39.6) ... [59.4, 69.3) < [69.3, 79.2) < [79.2, 89.1) < [89.1, 99.099)],
 array([  0.   ,   9.9  ,  19.8  ,  29.7  ,  39.6  ,  49.5  ,  59.4  ,
         69.3  ,  79.2  ,  89.1  ,  99.099]))
import time
import random

s = []
i = time.time()
while i < time.time() + 100:
    i += 1
    s.append([i + random.normalvariate(0, 1) * 2000, random.randint(0, 10), random.random()])
df = pd.DataFrame(s , columns = ["dt", "val1", "val2"])

print df.head()

s = pd.cut(df.dt, 10, right = False, retbins = True)

freq = s[0].value_counts().values
dt = s[1].tolist()[:10]
print freq
print dt

df_freq = pd.DataFrame({"dt" : dt, "freq" : freq})
df_freq.dt = df_freq.dt.astype("datetime64[s]")
df_freq = df_freq.set_index("dt")

print df_freq.head().to_string()
             dt  val1      val2
0  1.471485e+09     6  0.231597
1  1.471489e+09     4  0.111525
2  1.471485e+09    10  0.458811
3  1.471483e+09    10  0.027168
4  1.471487e+09     9  0.275005
[22 16 14 13 11  8  8  4  3  2]
[1471481582.4727995, 1471482543.2894208, 1471483504.1060421, 1471484464.9226635, 1471485425.7392848, 1471486386.555906, 1471487347.3725274, 1471488308.1891487, 1471489269.00577, 1471490229.8223913]
                     freq
dt                       
2016-08-18 00:53:02    22
2016-08-18 01:09:03    16
2016-08-18 01:25:04    14
2016-08-18 01:41:04    13
2016-08-18 01:57:05    11
display_charts(df_freq, title="my first test")

alt text

2. static plot in flask

To show the static highcharts plot through flask, a simple way is to prepare the data in python code, and render to the html page. The following is an example.

The structure of the dir will be like /app.py /static/main.css /static/js/main.js /templates/index.html

2.1 app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
@app.route('/index')
def index(chartID = 'chart_ID', chart_type = 'bar', chart_height = 350):
    chart = {"renderTo": chartID, "type": chart_type, "height": chart_height,}
    series = [{"name": 'Label1', "data": [1,2,3]}, {"name": 'Label2', "data": [4, 5, 6]}]
    title = {"text": 'My Title'}
    xAxis = {"categories": ['xAxis Data1', 'xAxis Data2', 'xAxis Data3']}
    yAxis = {"title": {"text": 'yAxis Label'}}
    return render_template('index.html', chartID=chartID, chart=chart, series=series, title=title, xAxis=xAxis, yAxis=yAxis)

if __name__ == "__main__":
    app.run(debug = True, host='0.0.0.0', port=8080, passthrough_errors=True)

2.2 index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="description" content="">
        <meta name="keywords" content="">
        <meta name="author" content="">
        <meta charset="utf-8">

        <!-- <link href="../static/css/main.css" rel="stylesheet" type="text/css" /> -->

        <!-- SUPPORT FOR IE6-8 OF HTML5 ELEMENTS -->
        <!--[if lt IE 9]>
                    <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
            <![endif]-->

        <!-- <link rel="shortcut icon" href="{{ url_for('static', filename='ico/favicon.ico') }}"> -->
        {% block head %}
            <title>{% block title %} Title!{% endblock %}</title>
        {% endblock %}
    </head>

    <body>
        <div id={{ chartID|safe }} class="chart" style="height: 100px; width: 500px"></div>
        <script>
            var chart_id = {{ chartID|safe }}
            var series = {{ series|safe }}
            var title = {{ title|safe }}
            var xAxis = {{ xAxis|safe }}
            var yAxis = {{ yAxis|safe }}
            var chart = {{ chart|safe }}
        </script>
            <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
            <script src="http://code.highcharts.com/highcharts.js"></script>
            <script src="../static/js/main.js"></script>
    </body>
</html>

2.3 main.css

* {
    margin: 20;
    padding: 100;
}
header {
    background-color:rgba(33, 33, 33, 0.9);
    color:#ffffff;
    display:block;
    font: 14px/1.3 Arial,sans-serif;
    height:50px;
    position:relative;
}
header h2{
    font-size: 22px;
    margin: 0px auto;
    padding: 10px 0;
    width: 80%;
    text-align: center;
}
header a, a:visited {
    text-decoration:none;
    color:#fcfcfc;
}
body {
    background:url("background.jpg") no-repeat scroll top center transparent;
}
.actions, .chart {
    margin: 15px auto;
    width: 34px;
}
button {
    background: none repeat scroll 0 0 #E3E3E3;
    border: 1px solid #BBBBBB;
    border-radius: 3px 3px 3px 3px;
    box-shadow: 0 0 1px 1px #F6F6F6 inset;
    color: #333333;
    font: bold 12px;
    margin: 0 5px;
    padding: 8px 0 9px;
    text-align: center;
    text-shadow: 0 1px 0 #FFFFFF;
    width: 150px;
}
button:hover {
    background: none repeat scroll 0 0 #D9D9D9;
    box-shadow: 0 0 1px 1px #EAEAEA inset;
    color: #222222;
    cursor: pointer;
}
button:active {
    background: none repeat scroll 0 0 #D0D0D0;
    box-shadow: 0 0 1px 1px #E3E3E3 inset;
    color: #000000;
}

2.4 main.js

$(document).ready(function() {
    $(chart_id).highcharts({
        chart: chart,
        title: title,
        xAxis: xAxis,
        yAxis: yAxis,
        series: series
    });
});

3. An example of system monitor: Memory Usage Plot

The folder structure is like /app.py /python_monitor.py * /templates/mon.html

The process is to catch the memory info in /proc/meminfo and save the data in mysql db. So first, we need to create the mysql db and tables.

3.0 create mysql db and table

mysql> create database falcon character set utf8;
Query OK, 1 row affected (0.00 sec)
CREATE TABLE `stat` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `host` varchar(256) DEFAULT NULL,
  `mem_free` int(11) DEFAULT NULL,
  `mem_usage` int(11) DEFAULT NULL,
  `mem_total` int(11) DEFAULT NULL,
  `load_avg` varchar(128) DEFAULT NULL,
  `time` bigint(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `host` (`host`(255))
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

3.1 app.py

on the web server side, it will use flask.

Once there is data post the page, python will run to insert that data into mysql table stat.

Once the page is refreshed, GET method will be activated and the function getdata() will run to pull data from mysql db and data will be dumped to json file for highcharts to call.

import sys
sys.path.append('/usr/lib/python2.7/dist-packages')

import MySQLdb as mysql
import json
from flask import Flask, request, render_template

'''
run sql to create table

CREATE TABLE `stat` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `host` varchar(256) DEFAULT NULL,
  `mem_free` int(11) DEFAULT NULL,
  `mem_usage` int(11) DEFAULT NULL,
  `mem_total` int(11) DEFAULT NULL,
  `load_avg` varchar(128) DEFAULT NULL,
  `time` bigint(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `host` (`host`(255))
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

'''



app = Flask(__name__)

db = mysql.connect(user="root", passwd="password", db="twitterdb", charset="utf8")
db.autocommit(True)
c = db.cursor()

@app.route("/", methods=["GET", "POST"])
def hello():
    sql = ""
    if request.method == "POST":
        data = request.json
        try:
            sql = "INSERT INTO `stat` (`host`,`mem_free`,`mem_usage`,`mem_total`,`load_avg`,`time`) VALUES('%s', '%d', '%d', '%d', '%s', '%d')" % (data['Host'], data['MemFree'], data['MemUsage'], data['MemTotal'], data['LoadAvg'], int(data['Time']))
            ret = c.execute(sql)
        except mysql.IntegrityError:
            pass
        return "OK"
    else:
        return render_template("mon.html")

@app.route("/data", methods=["GET"])
def getdata():
    c.execute("SELECT `time`,`mem_usage` FROM `stat`")
    ones = [[i[0]*1000, i[1]] for i in c.fetchall()]
    return "%s(%s);" % (request.args.get('callback'), json.dumps(ones))

if __name__ == "__main__":
    app.run(host="localhost", port=8888, debug=True)

3.2 python_monitor.py

This will read the /proc/meminfo data and dump data to the web page.

#!/usr/bin/env python
#coding=utf-8
import inspect
import time
import urllib, urllib2
import json
import socket
class mon:
    def __init__(self):
        self.data = {}
    def getTime(self):
        return str(int(time.time()) + 8 * 3600)
    def getHost(self):
        return socket.gethostname()
    def getLoadAvg(self):
        with open('/proc/loadavg') as load_open:
            a = load_open.read().split()[:3]
            return ','.join(a)
    def getMemTotal(self):
        with open('/proc/meminfo') as mem_open:
            a = int(mem_open.readline().split()[1])
            return a / 1024
    def getMemUsage(self, noBufferCache=True):
        if noBufferCache:
            with open('/proc/meminfo') as mem_open:
                T = int(mem_open.readline().split()[1])
                F = int(mem_open.readline().split()[1])
                B = int(mem_open.readline().split()[1])
                C = int(mem_open.readline().split()[1])
                return (T-F-B-C)/1024
        else:
            with open('/proc/meminfo') as mem_open:
                a = int(mem_open.readline().split()[1]) - int(mem_open.readline().split()[1])
                return a / 1024
    def getMemFree(self, noBufferCache=True):
        if noBufferCache:
            with open('/proc/meminfo') as mem_open:
                T = int(mem_open.readline().split()[1])
                F = int(mem_open.readline().split()[1])
                B = int(mem_open.readline().split()[1])
                C = int(mem_open.readline().split()[1])
                return (F+B+C)/1024
        else:
            with open('/proc/meminfo') as mem_open:
                mem_open.readline()
                a = int(mem_open.readline().split()[1])
                return a / 1024
    def runAllGet(self):
        for fun in inspect.getmembers(self, predicate=inspect.ismethod):
            if fun[0][:3] == 'get':
                self.data[fun[0][3:]] = fun[1]()
        return self.data

if __name__ == "__main__":
    while True:
        m = mon()
        data = m.runAllGet()
        print "data is %s " %(data)
        req = urllib2.Request("http://localhost:8888", json.dumps(data), {'Content-Type': 'application/json'})
        f = urllib2.urlopen(req)
        print f
        response = f.read()
        print "response is %s" %(response)
        f.close()
        time.sleep(5)

3.3 mon.html

This will use highcharts.

[root@91it templates]# cat mon.html
<title>memory monitor</title>
<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Highstock Example</title>
        <!-- <script type="text/javascript" src="{{ url_for('static', filename='jquery.min.js') }}"></script> -->
        <script type="text/javascript" src="http://ajax.useso.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
        <style type="text/css">
${demo.css}
        </style>
        <script type="text/javascript">
$(function () {
    $.getJSON('/data?callback=?', function (data) {
        // Create the chart
        $('#container').highcharts('StockChart', {
            rangeSelector: {
                inputEnabled: $('#container').width() > 480,
                selected: 1
            },
            title: {
                text: 'memory monitor'
            },
            series: [{
                name: 'memory monitor',
                data: data,
                type: 'spline',
                tooltip: {
                    valueDecimals: 2
                }
            }]
        });
    });
});
        </script>
    </head>
    <body>
<!-- <script src="{{ url_for('static', filename='highstock.js') }}"></script> -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/highstock/2.0.4/highstock.js"></script>
<!-- <script src="{{ url_for('static', filename='exporting.js') }}"></script> -->
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="height: 400px"></div>
    </body>
</html>

first run python app.py and then run nohup python python_monitor.py > null 2>&1 &. After it is run, input http://localhost:8888/ to check the result and http://localhost:8888/data to view data.

alt text

Reference

  1. pandas-highcharts

  2. Using Flask to output Python data to High Charts

  3. Python运维三十六式:用Python写一个简单的监控系统

  4. highcharts live data