Chart Pandas DataFrame columns using Holoviews DynamicMap served via Flask - python

Following this tutorial, I am trying to visualise a dataset using Holoviews instead of Bokeh (sample data available here as a CSV file), serving the results using Flask. I decided to use Flask and not Bokeh Server because I am building a larger workflow using the former.
My code is the following:
from flask import Flask, render_template, request
import numpy as np
import pandas as pd
from datetime import datetime
from bokeh.embed import components
from bokeh.io import curdoc
import holoviews as hv
hv.extension("bokeh")
app = Flask(__name__)
renderer = hv.renderer('bokeh')
infile = "./uploads/test.csv"
def loadRegionData(regionProperty, **kwargs):
df = pd.read_csv(infile, parse_dates=['Datetime'])
df1 = df[regionProperty]
df = pd.concat([df['Datetime'],df1], axis=1)
return hv.Curve(df)
colNames = ((pd.read_csv(infile, nrows=1)).drop(['Datetime'], axis=1)).columns.values
dmap = hv.DynamicMap(loadRegionData, kdims='RegionProperty').redim.values(RegionProperty=colNames)
hvplot = renderer.get_plot(dmap)
plot = hvplot.state
plot.name = 'plot'
curdoc().add_root(plot)
#app.route("/")
def index():
# Embed plot into HTML via Flask Render
script, div = components(plot)
return render_template("index.html", script=script, div=div)
if __name__ == '__main__':
app.run(port=5000, debug=True)
I am running into the following (unrelated issues)
When I deploy using Flask, the dropdowns to select the columns do not appear. I suspect that is because I am not returning/referring to the correct variables from the index() function into my index.html:
<html>
<head>
<link
href="http://cdn.bokeh.org/bokeh/release/bokeh-1.0.2.min.css"
rel="stylesheet" type="text/css">
<link
href="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.0.2.min.css"
rel="stylesheet" type="text/css">
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-1.0.2.min.js"></script>
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.0.2.min.js"></script>
</head>
<body>
<h1>Holoview test</h1>
{{ script|safe }}
{{ div|safe }}
</body>
</html>
How can I get Flask to also show the dropdown selector?
An unrelated issue which I found when I tested this app using Bokeh Server, and which could also arise in the Flask implementation, is that the scales do not adjust dynamically based on my column selection. Perhaps this can go as a separate question on SO, but I thought to include it here for now to keep things together.

Related

Geoviews map 403 load error in python flask Application

I am trying to generate a map with data to be displayed in a flask application. All that is displayed when I run the app is Figure(id='1021', ...). I also get this error in console: [Error] Failed to load resource: the server responded with a status of 403 () (bokeh.min.js.map, line 0). Any Ideas?
app.py
from flask import Flask, render_template
import geoviews as gv
import project_4 as map
import holoviews as hv
import geoviews.tile_sources as gvts
import pandas as pd
from bokeh.models import HoverTool
from geoviews import dim, opts
import numpy as np
app = Flask(__name__)
#app.route("/")
def index():
# Load the data and create the GeoViews points
college_data = pd.read_csv('updated_locations.csv')
college_gv_points = gv.Points(college_data, ['longitude', 'latitude'], ['university', 'num_students_attended'])
# Create the hover tool
tooltips = [('Unviversity', '#university'),
('Attendees', '#num_students_attended'),]
hover = HoverTool(tooltips=tooltips)
# Create the light and dark plots
light_plot = (gvts.CartoLight * college_gv_points).opts(
opts.Points(alpha=0.3,
hover_line_color='black',
color = 'blue', line_color='black', xaxis=None, yaxis=None,
tools=[hover],size=np.sqrt(dim('num_students_attended'))*2,
hover_fill_color='blue', hover_fill_alpha=0.2))
dark_plot = (gvts.CartoDark.options(alpha=0.8) * college_gv_points).opts(
opts.Points(alpha=0.6,
hover_line_color='black',
color = 'orange', line_color=None, xaxis=None, yaxis=None,
tools=[hover],size=np.sqrt(dim('num_students_attended'))*2,
hover_fill_color='orange', hover_fill_alpha=0.4))
# Render the map using Holoviews
hv_points = hv.render(dark_plot)
return render_template('index.html', plot=hv_points)
if __name__ == '__main__':
app.run(port=8000, debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<link
href="http://cdn.pydata.org/bokeh/release/bokeh-1.3.4.min.css"
rel="stylesheet"
type="text/css"
/>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.3.4.min.js"></script>
</head>
<body>
{{ plot | safe }}
</body>
</html>

Flask rendering previous version of CSS [duplicate]

This question already has an answer here:
Flask css not updating [closed]
(1 answer)
Closed 2 years ago.
Flask app is rendering a previous version of my css file (I have saved and made changes, but when I inspect page, the css file shown is a previous version). Maybe previous version of css file is somehow stuck in the cache? I tried restarting browser, no luck .
Here is the code:
Part of app.py file (the part where I'm rendering the HTML file):
from flask import Flask,render_template,url_for,request
from twilio.rest import Client
app = Flask(__name__, template_folder='templates')
app.static_folder = 'static'
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
pd.core.common.is_list_like = pd.api.types.is_list_like
import pandas_datareader as pdr
#import yahoo finance api fixer
import fix_yahoo_finance as fyf
from pandas_datareader import data as pdr
from datetime import datetime, timedelta
#app.route('/')
def home():
return render_template('index.html')
if __name__ == "__main__":
app.run(debug=True)
index.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}" />
</head>
<body>
<h1>Ticker Predictor</h1>
<h2>Using Machine Learning to Predict Tomorrow's Stock Prices</h2>
<br>
<h3>Simply enter a valid stock ticker below and hit the predict button to receive a text message of tomorrow's predicted opening price of that stock within around 10 minutes!</h3>
<!-- Main Input For Receiving Query to our ML -->
<form action="{{ url_for('predict')}}"method="post">
<input type="text" placeholder="Input Stock Ticker Here" required="required" />
<button type="submit">Predict</button>
</form>
{{ prediction_text }}
</body>
</html>
And here is the file structure:
TickerPredictor
|--static/
|--styles.css
|--templates/
|--index.html
|--app.py
Any help would be much appreciated! Thank you!
your app.py should be something like this
from flask import Flask,render_template
app = Flask(__name__)
#app.route('/')
def home():
return render_template('index.html')
if __name__ == "__main__":
DEBUG = True
HOST = '0.0.0.0'
app.run(debug=DEBUG, host=HOST)
In your app.py file you did not mention the host. Update your app.py file and it should work.
Thanks to help from Rahul and stackoverflow.com/questions/21714653/flask-css-not-updating , I just performed a hard reload in my browser to clear the cache (CMD + SHIFT + R). In other words, the previous version of the css file was getting stored in the browser cache, clearing the cache gets rid of previous css file version and most recent version is then displayed (which is obviously what I want). Thanks everyone!

Flask rendering a unique file name in browser

I am attempting to make an app where a user can upload a CSV file of data and view the data thru the browser on a different flask route which would also a show the plot of the data.
I having issues in my code trying to show the plot of the data. (referencing the static file .png) I can get the pandas dataframe to HTML to work, but in my table.html file I am trying to reference a png plot created with matplot lib and saved to a static directory.
<img src="{{url_for('static', filename=filename)}}" />
All of this experimenting is due to cache issues with the browser so I am creating a unique filename with next_file_name function, and I think this is where I am getting screwed up in the HTML & Jinja trying to reference this unique file name.. I am hoping that a unique filename may be a fix for the cache issues I am observing. The png files are saving properly with the function plot0, plot1, plot2, plot3, etc...
I was hoping to be able to create something where I can repeat the process over & over of analyzing new data and getting retrieving a fresh new plot of the data. Any tips help, thanks
from flask import Flask, make_response, request, render_template
from werkzeug.utils import secure_filename
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import time
app = Flask(__name__, static_url_path='/static')
num = 0
def next_file_name(num):
return 'static/plot%d.png' % num
#app.route('/')
def form():
return render_template('form.html')
#app.route('/transform', methods=["POST"])
def transform_view():
global num
f = request.files['data_file']
filename = secure_filename(f.filename)
f.save(filename)
df = pd.read_csv(filename, index_col='Date', parse_dates=True)
OAT = pd.Series(df['OAT'])
RAT = pd.Series(df['RAT'])
MAT = pd.Series(df['MAT'])
df_OATrat = (OAT - RAT)
df_MATrat = (MAT - RAT)
plt.scatter(df_OATrat,df_MATrat, color='grey', marker='+')
plt.xlabel('OAT-RAT')
plt.ylabel('MAT-RAT')
plt.title('Economizer Diagnostics')
plt.plot([0,-18],[0,-18], color='green', label='100% OSA during ideal conditions')
plt.plot([0,20],[0,5], color='red', label='Minimum OSA in cooling mode')
plt.plot([0,-38],[0,-9.5], color='blue', label='Minimum OSA in heating mode')
plt.plot([0,0],[-20,10], color='black')
plt.plot([-30,20],[0,0], color='black')
#plt.legend()
plt.text(-3, -28, time.ctime(), fontsize=9)
pic = next_file_name(num)
plt.savefig(pic)
num+=1
resp = make_response(render_template('table.html', tables=[df.to_html(classes='data')], titles=df.columns.values, filename='pic'))
resp.cache_control.no_cache = True
return resp
if __name__ == '__main__':
app.run(debug=True)
table.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv='cache-control' content='no-cache'>
<meta http-equiv='expires' content='0'>
<meta http-equiv='pragma' content='no-cache'>
<title>Title</title>
</head>
<body>
<h1>Economizer Data Plot</h1>
<img src="{{url_for('static', filename=filename)}}" />
</form>
{% for table in tables %}
{{ table|safe }}
{% endfor %}
</body>
</html>
You made pic a string instead of using it's value
... filename=pic
If it's a caching issue, it would be easier to either add automatic cache busting to your image files or disable cache with an after-request callback, which triggers the execution of a function at the end of a request.
#app.after_request
def add_header(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "0"
response.headers['Cache-Control'] = 'public, max-age=0'
return response
See this solution for more information.

Trouble displaying Bokeh toolbar

I'm new to Bokeh and Flask python in general but i have managed to create a graph and then output it on my browser via flask. The only problem is that im not getting the "Bokeh tool bar" next to my graph.
My code looks like this
from flask import Flask, render_template, request
import pandas as pd
import csv
from bokeh.plotting import figure
from bokeh.io import show
from bokeh.embed import components
from bokeh.models import Range1d
from bokeh.resources import CDN
app = Flask(__name__)
# Create the main plot
def create_figure():
xvals = []
yvals = []
with open('test.csv') as csvfile:
readCSV = csv.reader(csvfile, delimiter=',')
for row in readCSV:
xvalue = row[0]
yvalue = row[1]
xvals.append(xvalue)
yvals.append(yvalue)
p = figure(plot_width=400, plot_height=400, x_range=(0, 20))
p.y_range = Range1d(0, 15)
p.circle(xvals, yvals, size=10)
return p
# Index page
#app.route('/')
def index():
plot = create_figure()
script, div = components(plot)
cdn_js = CDN.js_files[0]
cdn_css = CDN.css_files[0]
return render_template("index.html", script=script, div=div,
cdn_js=cdn_js,
cdn_css=cdn_css)
# With debug=True, Flask server will auto-reload
# when there are code changes
if __name__ == '__main__':
app.run(port=5000, debug=True)
and my index.html code looks like this:
<html>
<head>
<link href={{ cdn_css|safe }} type="text/css" />
<script type="text/javascript" src={{ cdn_js|safe }}></script>
</head>
<body>
<H1>First</H1>
{{ script|safe }}
{{ div|safe }}
</body>
</html>
Am i missing something? When i output the graph to a output_file i get the toolbar. Any help would be appreciated.
Most likely this is this https://github.com/bokeh/bokeh/issues/7497 issue. The available workarounds, as I posted in the thread, are as follows:
There are two options. If you want to keep the toolbar as part of a plot, then you will have to create a ToolbarPanel manually and add it with add_layout() to a plot. Alternatively you can have a toolbar detached from a plot, as an element of a bigger layout, like it happens with grid plots. In both cases the key is to set plot.toolbar_location = None, to disable creation of the default ToolbarPanel.
Please follow this issue so that you can be aware of future developments.
I had the same problem. I can't explain the reason, but this example works: realpython github

Return a response with an download option on the same HTML page using flask

I have a basic flask app where data frames are formed from two CSVs and some transformations happen and on the HTML page , a final result dataframe can be seen in a tabular format. It works fine till here.
Apart from that, I also want the user to have an option to download the same table as a CSV.
Below is my flask code:
from flask import *
import pandas as pd
app = Flask(__name__)
#app.route("/tables")
def show_tables():
df1 = pd.read_csv('daily.csv')
df2 = pd.read_csv('companies.csv')
df1['date']= pd.to_datetime(df1['date'], format='%m/%d/%y')
df3 = pd.merge(df1,df2,how='left',on='id')
dates = pd.DataFrame({"date": pd.date_range("2017-01-01", "2017-01-10")})
df4 = (df3.groupby(['id', 'name'])['date', 'value']
.apply(lambda g: g.merge(dates, how="outer"))
.fillna(0)
.reset_index(level=[0,1])
.reset_index(drop=True))
df4 = df4.sort_values(by=['id','date'])
df4.value = df4.value.astype(int)
df4['difference'] = df4.groupby('id')['value'].diff()
return render_template('view.html',tables=[df4.to_html(classes='Company_data')],
titles = [ 'Company_data'],filename=df4.to_csv())
#app.route('/tables_download/<filename>')
def tables_download(filename):
return response(filename) //--right way to pass the csv file?
if __name__ == "__main__":
app.run()
Below is my HTML code:
<!doctype html>
<title>Simple tables</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<h1>Company data</h1>
{% for table in tables %}
<h2>{{titles[loop.index]}}</h2>
{{ table|safe }}
{% endfor %}
</div>
Download
On my HTML page, I don't even see the Download option.
Struggling to figure out what's wrong so looking for help
As documented in the Flask API, I believe that send_file or send_from_directory would be the way to implement this.
#app.route('/uploads/<path:filename>')
def download_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename, as_attachment=True)
These are both documented on http://flask.pocoo.org/docs/0.12/api/
send_from_directory is more secure (if used correctly) as it limits the file available to download to just those in a specific directory preventing any 'hackers' from downloading your private information.

Categories