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>
Related
I am setting up a webserver with flask and seaborn/matplotlib. There I am creating and assigning a variable in Python img_buffer of type IOBytes to represent a graph.
Then I am transforming this data into an str img_tag:
...
import base64
str_equivalent_image = base64.b64encode(img_buffer.getvalue()).decode()
img_tag = "<img src='data:image/png;base64," + str_equivalent_image + "'/>"
Now I want to use this img_tag in the corresponding index.html to display the image. How to I do that? The following did not work:
...
<body>
<h1>Graph</h1>
{img_tag}
</body>
</html>
This is the code I used. It is reduced as much as possible. The page is updated every 0,5s. Later the graphic should be updated as well on the basis of dynamic data I read out from a URL, but this is not yet implemented:
server.py:
from gevent import monkey; monkey.patch_all()
from flask import Flask, Response, render_template, stream_with_context, url_for, send_file, render_template
from gevent.pywsgi import WSGIServer
import json
import time
import io
import base64
from requests.exceptions import HTTPError
import matplotlib as mpl
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import matplotlib.pyplot as plt
import seaborn as sns
fig,ax=plt.subplots(figsize=(6,6))
ax=sns.set(style="darkgrid")
x=[i for i in range(100)]
y=[i for i in range(100)]
app = Flask(__name__)
counter = 100
#app.route("/")
def render_index():
return render_template("index.html")
#app.route('/getVisualizeData')
def getVisualizeData():
sns.lineplot(x,y)
canvas=FigureCanvas(fig)
img = io.BytesIO()
fig.savefig(img)
img.seek(0)
return img
#app.route("/listen")
def listen():
def respond_to_client():
while True:
img_buffer=getVisualizeData()
str_equivalent_image = base64.b64encode(img_buffer.getvalue()).decode()
img_tag = "<img src='data:image/png;base64," + str_equivalent_image + "'/>"
_data = json.dumps({"img_tag":img_tag})
yield f"id: 1\ndata: {_data}\nevent: online\n\n"
time.sleep(0.5)
return Response(respond_to_client(), mimetype='text/event-stream')
if __name__ == "__main__":
http_server = WSGIServer(("localhost", 8080), app)
http_server.serve_forever()
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APP</title>
</head>
<body>
<h1>Graph</h1>
{img_tag}
<script>
var eventSource = new EventSource("/listen")
eventSource.addEventListener("message", function(e) {
console.log(e.data)
}, false)
eventSource.addEventListener("online", function(e) {
data = JSON.parse(e.data)
document.querySelector("#img_tag").innerText = data.img_tag
}, true)
</script>
</body>
</html>
Try using double brackets
<body>
<h1>Graph<h1>
{{img_tag}}
</body>
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.
I am trying to display the plotted figure using the web browser. But the render template in return statement is throwing error. Below is the code used and the error received.
The python code:
from flask import Flask, render_template, Response
import io
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import numpy as np
app = Flask(__name__,template_folder = 'D:/DATA_SETS')
#app.route('/html_example')
def myplot():
with app.app_context():
plt.figure()
t = range(0,6,1)
x= np.sin(t)
plt.plot(t,x)
plt.savefig('D:/DATA_SETS/new_plot.png')
return Response(render_template('template_1.html', name = 'new_plot', url ='D:/DATA_SETS/new_plot.png'))
f = myplot()
if 1 == 1:
app.run(debug=True)
The template template_1.html
<!doctype html>
<html>
<body>
<h1>Performance</h1>
<p>{{ name }}</p>
<img src={{ url}} alt="Chart" height="42" width="42">
</body>
</html>
Expected : The image return from function should be displayed in the HTML format written.
Current : The webpage is displaying 'Not Found Error'.
Webpage looking at is 'http://localhost:5000/html_example'
You need to wrap your code into with app.app_context function. Your code will be similar to this:
app = Flask(__name__,template_folder = 'templates')
#app.route('/')
def myplot():
with app.app_context():
plt.figure()
t = range(0,6,1)
x= np.sin(t)
plt.plot(t,x)
plt.savefig('D:/DATA_SETS/new_plot.png')
return Response(render_template('template_1.html', name = 'new_plot',
url ='D:/DATA_SETS/new_plot.png'))
if __name__ == '__main__':
app.run(debug=True)
The problem is in the return statement. It should be
return render_template('template_1.html', name = 'new_plot', url ='D:/DATA_SETS/new_plot.png'))
and you should not be doing
f= myplot()
for producing the image.
when you will hit the URL localhost:5000/ it will automatically do the process of producing image and serve it to frontend.
Update:
Directory Structure:
main.py
from flask import Flask, render_template, Response
import io
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import numpy as np
app = Flask(__name__,static_url_path="/folder/",static_folder="folder")
#app.route('/')
def myplot():
plt.figure()
t = range(0,6,1)
x= np.sin(t)
plt.plot(t,x)
plt.savefig(app.static_folder+'/'+'new_plot.png')
return render_template('template_1.html', name = 'new_plot', filename ='new_plot.png')
if __name__ == "__main__":
app.run(debug=True)
template_1.html
<!doctype html>
<html>
<body>
<h1>Performance</h1>
<p>{{ name }}</p>
<img src="{{ url_for('static', filename=filename) }}" alt="Chart" height="100" width="100">
</body>
</html>
Output:
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.
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