Compare commits
10 commits
57af966ebe
...
63ff2e10dc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63ff2e10dc | ||
|
|
a84fb3a90d | ||
|
|
6e321c5de4 | ||
|
|
2132e463c1 | ||
|
|
bd965a1599 | ||
|
|
46b051060c | ||
|
|
703d40c46c | ||
|
|
890df24abd | ||
|
|
361ac6ebbf | ||
|
|
c1e5862f83 |
12 changed files with 889 additions and 23 deletions
|
|
@ -1,6 +1,9 @@
|
||||||
# flask-chartist-example
|
# flask-chartist-example
|
||||||
Learning more about flask and javascript
|
Learning more about flask and javascript
|
||||||
|
|
||||||
|
This app is currently running on [heroku](http://still-beyond-87368.herokuapp.com/chartist)
|
||||||
|
|
||||||
|
|
||||||
This project will create a flask dashboard with a javascript library, and the charts will update without refreshing the page. The charts will not be polished or excitin, this is just to explore how to create a working dashboard in python with minimal javascript to refresh the data.
|
This project will create a flask dashboard with a javascript library, and the charts will update without refreshing the page. The charts will not be polished or excitin, this is just to explore how to create a working dashboard in python with minimal javascript to refresh the data.
|
||||||
|
|
||||||
## Inspiration
|
## Inspiration
|
||||||
|
|
|
||||||
37
app/app.py
37
app/app.py
|
|
@ -14,6 +14,8 @@ from matplotlib.pyplot import rcParams
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import StringField, SelectField
|
from wtforms import StringField, SelectField
|
||||||
|
|
||||||
|
from bokeh.plotting import figure
|
||||||
|
from bokeh.embed import components
|
||||||
|
|
||||||
df = pd.read_csv(os.path.join(settings.data_dir, 'pop_by_country_long_form.csv'))
|
df = pd.read_csv(os.path.join(settings.data_dir, 'pop_by_country_long_form.csv'))
|
||||||
df['Year'] = df['Year'].str[4:].astype(int)
|
df['Year'] = df['Year'].str[4:].astype(int)
|
||||||
|
|
@ -85,6 +87,14 @@ def matplotlib_plot():
|
||||||
scripts = render_template('matplotlib.js')
|
scripts = render_template('matplotlib.js')
|
||||||
return render_template('home.html',head_scripts=head_scripts, body=body, scripts=scripts)
|
return render_template('home.html',head_scripts=head_scripts, body=body, scripts=scripts)
|
||||||
|
|
||||||
|
@app.route('/bokeh', methods=['GET', 'POST'])
|
||||||
|
def bokeh_plot():
|
||||||
|
form = MyForm()
|
||||||
|
head_scripts = render_template('bokeh_head_scripts.html')
|
||||||
|
body = render_template('bokeh.html', form=form)
|
||||||
|
scripts = render_template('bokeh.js')
|
||||||
|
return render_template('home.html',head_scripts=head_scripts, body=body, scripts=scripts)
|
||||||
|
|
||||||
@app.route('/chartist', methods=['GET', 'POST'])
|
@app.route('/chartist', methods=['GET', 'POST'])
|
||||||
def chartist():
|
def chartist():
|
||||||
form = MyForm()
|
form = MyForm()
|
||||||
|
|
@ -93,6 +103,14 @@ def chartist():
|
||||||
scripts = render_template('chartist.js')
|
scripts = render_template('chartist.js')
|
||||||
return render_template('home.html', head_scripts=head_scripts, body=body, scripts=scripts)
|
return render_template('home.html', head_scripts=head_scripts, body=body, scripts=scripts)
|
||||||
|
|
||||||
|
@app.route('/c3', methods=['GET', 'POST'])
|
||||||
|
def c3_route():
|
||||||
|
form = MyForm()
|
||||||
|
head_scripts = render_template('c3_head_scripts.html')
|
||||||
|
body = render_template('c3.html', form=form)
|
||||||
|
scripts = render_template('c3_update.js')
|
||||||
|
return render_template('home.html', head_scripts=head_scripts, body=body, scripts=scripts)
|
||||||
|
|
||||||
@app.route('/chartist1', methods=['GET', 'POST'])
|
@app.route('/chartist1', methods=['GET', 'POST'])
|
||||||
def chartist1():
|
def chartist1():
|
||||||
form = MyForm()
|
form = MyForm()
|
||||||
|
|
@ -111,6 +129,23 @@ def mpl(nation):
|
||||||
hide_spines(ax)
|
hide_spines(ax)
|
||||||
return jsonify({'src': fig_to_html(ax.figure)})
|
return jsonify({'src': fig_to_html(ax.figure)})
|
||||||
|
|
||||||
|
@app.route('/pybokeh/<string:nation>')
|
||||||
|
def pybokeh(nation):
|
||||||
|
p = figure(title=nation, logo=None, tools="box_zoom,pan,wheel_zoom,reset",)
|
||||||
|
p.line(x=df2[nation].index, y=df2[nation].values)
|
||||||
|
script, div = components(p)
|
||||||
|
return jsonify({'script': script,
|
||||||
|
'div': div})
|
||||||
|
|
||||||
|
@app.route('/c3data/<string:nation>')
|
||||||
|
def c3_daata_route(nation):
|
||||||
|
|
||||||
|
data = {'columns':[[nation] + df2[nation].astype(float).tolist(), ['x'] + df2.index.astype(float).tolist()],
|
||||||
|
'colors': {nation: '#4286f4'},
|
||||||
|
'unload': "",}
|
||||||
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
port = int(os.environ.get("PORT", 5000))
|
port = int(os.environ.get("PORT", 5000))
|
||||||
app.run(host='0.0.0.0', port=port)
|
app.run(host='0.0.0.0', port=port, debug=True)
|
||||||
22
app/templates/bokeh.html
Normal file
22
app/templates/bokeh.html
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{% from "_render_field.html" import render_field %}
|
||||||
|
|
||||||
|
<h1>Population by Nation</h1><br>
|
||||||
|
data from
|
||||||
|
<a href="https://data.world/nrippner/population-by-country1980-2010">data.world</a>
|
||||||
|
<br><br>
|
||||||
|
Created with Flask and python bokeh.
|
||||||
|
Using the pure python causes the whole chart to be refreshed.
|
||||||
|
It would be possible to update the data only to avoid the chart completely redrawing on update.
|
||||||
|
|
||||||
|
<span class='nation' value='nation'></span>
|
||||||
|
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>World Population</legend>
|
||||||
|
{{ render_field(form.nation) }}
|
||||||
|
|
||||||
|
|
||||||
|
<div id="bokeh-pop-plot"></div>
|
||||||
|
<div id="bokeh-pop-script"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
19
app/templates/bokeh.js
Normal file
19
app/templates/bokeh.js
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
function updateChart(){
|
||||||
|
console.log('updated div/scripts')
|
||||||
|
var nation = $("#nation option:selected").text()
|
||||||
|
var updatedData = $.get('/pybokeh/'.concat(nation));
|
||||||
|
|
||||||
|
updatedData.done(function(results){
|
||||||
|
|
||||||
|
$("#bokeh-pop-plot").html(results.div);
|
||||||
|
$("#bokeh-pop-script").html(results.script);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$( document ).ready(updateChart)
|
||||||
|
$('#update').on('click', updateChart)
|
||||||
|
$('#nation').on('change', updateChart)
|
||||||
10
app/templates/bokeh_head_scripts.html
Normal file
10
app/templates/bokeh_head_scripts.html
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
||||||
|
<link
|
||||||
|
href="http://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css"
|
||||||
|
rel="stylesheet" type="text/css">
|
||||||
|
<link
|
||||||
|
href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css"
|
||||||
|
rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<script src="http://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js"></script>
|
||||||
18
app/templates/c3.html
Normal file
18
app/templates/c3.html
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{% from "_render_field.html" import render_field %}
|
||||||
|
|
||||||
|
|
||||||
|
<h1>Population by Nation</h1><br>
|
||||||
|
data from
|
||||||
|
<a href="https://data.world/nrippner/population-by-country1980-2010">data.world</a>
|
||||||
|
<br><br>
|
||||||
|
Created with Flask and python c3
|
||||||
|
|
||||||
|
<span class='nation' value='nation'></span>
|
||||||
|
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>World Population</legend>
|
||||||
|
{{ render_field(form.nation) }}
|
||||||
|
|
||||||
|
|
||||||
|
<div id='chart' style='width: 100%; height: 100%'></div>
|
||||||
6
app/templates/c3_head_scripts.html
Normal file
6
app/templates/c3_head_scripts.html
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<link href='https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.css' rel='stylesheet' type='text/css'/>
|
||||||
|
|
||||||
|
<!-- Load d3.js and c3.js -->
|
||||||
|
<script src='http://d3js.org/d3.v3.min.js' charset='utf-8'></script>
|
||||||
|
<script src='http://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.js'></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
||||||
154
app/templates/c3_update.js
Normal file
154
app/templates/c3_update.js
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
var data = {
|
||||||
|
'size': {
|
||||||
|
'height': 300
|
||||||
|
},
|
||||||
|
'data': {
|
||||||
|
'x': 'x',
|
||||||
|
'type': 'line',
|
||||||
|
'axes': {
|
||||||
|
'Australia': 'y',
|
||||||
|
'x': 'y'
|
||||||
|
},
|
||||||
|
'columns': [
|
||||||
|
[
|
||||||
|
'Australia',
|
||||||
|
'14.6159',
|
||||||
|
'14.92326',
|
||||||
|
'15.16178',
|
||||||
|
'15.34807',
|
||||||
|
'15.51048',
|
||||||
|
'15.69524',
|
||||||
|
'15.90042',
|
||||||
|
'16.13672',
|
||||||
|
'16.40073',
|
||||||
|
'16.6806',
|
||||||
|
'16.95624',
|
||||||
|
'17.20212',
|
||||||
|
'17.41893',
|
||||||
|
'17.6078',
|
||||||
|
'17.78109',
|
||||||
|
'17.97563',
|
||||||
|
'18.19585',
|
||||||
|
'18.41526',
|
||||||
|
'18.62084',
|
||||||
|
'18.83025',
|
||||||
|
'19.05319',
|
||||||
|
'19.29426',
|
||||||
|
'19.53494',
|
||||||
|
'19.76654',
|
||||||
|
'19.99508',
|
||||||
|
'20.23235',
|
||||||
|
'20.48947',
|
||||||
|
'20.74963',
|
||||||
|
'21.00731',
|
||||||
|
'21.26264',
|
||||||
|
'21.51575'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'x',
|
||||||
|
'1980',
|
||||||
|
'1981',
|
||||||
|
'1982',
|
||||||
|
'1983',
|
||||||
|
'1984',
|
||||||
|
'1985',
|
||||||
|
'1986',
|
||||||
|
'1987',
|
||||||
|
'1988',
|
||||||
|
'1989',
|
||||||
|
'1990',
|
||||||
|
'1991',
|
||||||
|
'1992',
|
||||||
|
'1993',
|
||||||
|
'1994',
|
||||||
|
'1995',
|
||||||
|
'1996',
|
||||||
|
'1997',
|
||||||
|
'1998',
|
||||||
|
'1999',
|
||||||
|
'2000',
|
||||||
|
'2001',
|
||||||
|
'2002',
|
||||||
|
'2003',
|
||||||
|
'2004',
|
||||||
|
'2005',
|
||||||
|
'2006',
|
||||||
|
'2007',
|
||||||
|
'2008',
|
||||||
|
'2009',
|
||||||
|
'2010'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'subchart': {
|
||||||
|
'show': false
|
||||||
|
},
|
||||||
|
'point': {
|
||||||
|
'show': false
|
||||||
|
},
|
||||||
|
'grid': {
|
||||||
|
'x': {
|
||||||
|
'show': false
|
||||||
|
},
|
||||||
|
'y': {
|
||||||
|
'show': false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'axis': {
|
||||||
|
'x': {
|
||||||
|
'tick': {
|
||||||
|
'count': 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'y': {
|
||||||
|
'tick': {
|
||||||
|
'format': ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'y2': {
|
||||||
|
'tick': {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'zoom': {}
|
||||||
|
};
|
||||||
|
data['axis']['y']['tick']['format'] = d3.format('')
|
||||||
|
data['axis']['y2']['tick']['format'] = d3.format('')
|
||||||
|
data['bindto']='#chart'
|
||||||
|
var chart = c3.generate(data);
|
||||||
|
|
||||||
|
function load_data(country) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.open('GET', 'http://127.0.0.1:5000/' + country);
|
||||||
|
xhr.send(null);
|
||||||
|
results = JSON.parse(xhr.responseText.result)
|
||||||
|
document.getElement
|
||||||
|
return data
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function updateChart(){
|
||||||
|
var nation = $("#nation option:selected").text()
|
||||||
|
|
||||||
|
var updatedData = $.get('/c3data/'.concat(nation));
|
||||||
|
updatedData.done(function(results){
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
labels: results.labels,
|
||||||
|
series: [
|
||||||
|
results.results
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
chart.load(updatedData.responseJSON)
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$( document ).ready(updateChart)
|
||||||
|
// $('#update').on('click', updateChart)
|
||||||
|
$('#nation').on('change', updateChart)
|
||||||
|
|
@ -34,10 +34,12 @@ body {margin:0;}
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="topnav">
|
<div class="topnav">
|
||||||
<a class="active" href="#home">Home</a>
|
|
||||||
<a href="/home">Home</a>
|
<a href="/home">Home</a>
|
||||||
|
<a href="http://www.blog.waylonwalker.com">Blog</a>
|
||||||
<a href="/chartist">Chartist</a>
|
<a href="/chartist">Chartist</a>
|
||||||
<a href="/matplotlib">Matplotlib</a>
|
<a href="/matplotlib">Matplotlib</a>
|
||||||
|
<a href="/bokeh">bokeh</a>
|
||||||
|
<a href="/c3">c3</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ body | safe }}
|
{{ body | safe }}
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,5 @@ function updateChartSrc(){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$( document ).ready(updateChartSrc)
|
||||||
$("#nation").on('change', updateChartSrc);
|
$("#nation").on('change', updateChartSrc);
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
||||||
# Requirements automatically generated by pigar.
|
# Requirements automatically generated by pigar.
|
||||||
# https://github.com/Damnever/pigar
|
# https://github.com/Damnever/pigar
|
||||||
|
|
||||||
Python == 3.6.1
|
bokeh
|
||||||
|
|
||||||
# app.py: 5
|
# app.py: 5
|
||||||
Flask == 0.12
|
Flask == 0.12
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue