I'm building my first web application using Flask. The concept of the web app is to manipulate and visualize data using Pandas and Bokeh. All of this has been relatively straight forward.
Now I'm at a point where I'm plotting information on a map of New York using GMapPlot. When I use output_file to show the plot I get the expected result.
expected
However when I try to return the plot to my own html template the map is empty.
actual
Here's my .py file (apologies for the messy code).
import pandas as pd
import datetime
from flask import render_template, request
from web_app.app import app
from bokeh.io import output_file, show
from bokeh.embed import components
from bokeh.models import (
GMapPlot, GMapOptions, ColumnDataSource, Circle, DataRange1d, PanTool, WheelZoomTool, BoxSelectTool
)
#app.route('/heatmap', methods=['GET', 'POST'])
def generate_heatmap():
date1 = datetime.datetime.strptime(request.form['startFilter'],"%Y-%m-%d").date()
date2 = datetime.datetime.strptime(request.form['stopFilter'], "%Y-%m-%d").date()
date2 += datetime.timedelta(days=1)
date_start = str(date1)
date_stop = str(date2)
df = pd.read_csv("...", sep=",")
df = df.set_index(['starttime'])
df = df.loc[date_start:date_stop]
start_lats = pd.Series(df['start station latitude']).unique()
stop_lats = pd.Series(df['end station latitude']).unique()
start_long = pd.Series(df['start station longitude']).unique()
stop_long = pd.Series(df['end station longitude']).unique()
lats = start_lats.tolist() + stop_lats.tolist()
long = start_long.tolist() + stop_long.tolist()
map_options = GMapOptions(lat=40.741557, lng=-73.990467, map_type="roadmap", zoom=11)
plot = GMapPlot(
x_range=DataRange1d(), y_range=DataRange1d(), map_options=map_options, title="NYC"
)
source = ColumnDataSource(
data=dict(
lat=lats,
lon=long,
)
)
circle = Circle(x="lon", y="lat", size=8, fill_color="blue", fill_alpha=0.8, line_color=None)
plot.add_glyph(source, circle)
plot.add_tools(PanTool(), WheelZoomTool(), BoxSelectTool())
output_file("gmap_plot.html")
show(plot)
script, div = components(plot)
return render_template('heatmap.html', script = script, div = div)
and my HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Heatmap</title>
<link rel="stylesheet" href="http://cdn.bokeh.org/bokeh/release/bokeh-0.11.1.min.css" type="text/css" />
<script type="text/javascript" src="http://cdn.bokeh.org/bokeh/release/bokeh-0.11.1.min.js"></script>
{{ script | safe }}
</head>
<body>
<div class='bokeh'>
{{ div | safe }}
</div>
</body>
I'm a beginner with both these tools, but I put them to work by doing:
from web_app.app import app
from flask import render_template
from bokeh.embed import file_html
from bokeh.plotting import figure
from bokeh.resources import CDN
#app.route('/example', methods=['GET'])
def example():
plot = figure()
plot.circle([1,2], [3,4])
html = file_html(plot, CDN)
return render_template('whatever.html', plot=html)
Then, using Jinja2 in the template:
{{ plot|safe }}
Related
This question is not a duplicate because I have stored the image address in a dataset and i need to retrieve those images that are present in my folder using a machine learning model, store it in a list and display those images in a browser I am creating a movie recommendation system in flask. I need to display poster image along with the name, but the browser doesn't seem to view the image. Here is my code:
app.py:
from flask import Flask,render_template
from flask import request
from sample import get_title_from_index
from sample import get_poster_from_index
from sample import similar_movies
from flask import jsonify
from PIL import Image
import array as arr
app = Flask(__name__)
#app.route('/', methods=['GET','POST'])
def predict():
if 'movie_input' in request.form:
movies = similar_movies(request.form['movie_input'])
i=0
print("Top 5 similar movies are:")
e1= []
p1=[]
for element in movies:
e1.insert(0,get_title_from_index(element[0]))
image= Image.open(get_poster_from_index(element[0]))
p1.insert(0,image)
i=i+1
if i>5:
break
else:
e1 = "No movies selected. Please, select something!"
p1 = ""
return render_template('predictor.html', movie=e1,poster=p1)
if __name__ == '__main__':
app.run(debug=True)
predictor.html:
<!doctype html>
<html>
<head>
</head>
<body>
<form method="POST">
<input type="text" name="movie_input" maxlength="500" >
<input type="submit" value="Submit" method="get" >
</form>
<p>
{{movie[0]}}
{{poster[0]}}
</p>
</body>
</html>
sample.py
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity
df = pd.read_csv("movies.csv")
def get_title_from_index(index):
return df[df.index == index]["title"].values[0]
def get_index_from_title(title):
return df[df.title == title]["index"].values[0]
def get_poster_from_index(index):
return df[df.index == index]["poster"].values[0]
def combine_features(row):
return row["keywords"]+"" +row["cast"]+""+row["genres"]+""+row["director"]
def similar_movies(movie_input):
features = ['keywords','cast','genres','director']
df["poster"] = df["poster"].fillna("")
for feature in features:
df[feature] = df[feature].fillna("") #filling all NaNs with blank string
df["combined_features"] = df.apply(combine_features,axis=1)
cv = CountVectorizer() #creating new CountVectorizer() object
count_matrix = cv.fit_transform(df["combined_features"])
cosine_sim = cosine_similarity(count_matrix)
movie_index = get_index_from_title(movie_input)
similar_movies = list(enumerate(cosine_sim[movie_index]))
sorted_similar_movies = sorted(similar_movies,key=lambda x:x[1],reverse=True)[1:]
return sorted_similar_movies
The browser view:
browser view
The browser view shows that the image is present but doesn't seems to view it.
dataset view
movie dataset view
folder view
flask folder
Please review my code and let me know the necessary changes.
You can't just output an image file into html. You have you have encode it an put it in an html IMG tag.
If you encode the image data into base64, you can do something like:
{{movie[0]}}
<img src="data:image/png;base64, {{poster[0]}}" />
For encoding the image, see Encoding an image file with base64
I'm using reportlab to create a pdf. I am using Reportlab Paragraphs. The problem is everytime i download it, it generates an empty txt.
I tested it without django and it works without a problem. If i'm using canvas it works but it doesn't suit what i need.
views.py
from django.http import HttpResponse
from django.shortcuts import render
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.pagesizes import letter
def genereaza_pdf(request):
if request.method == 'POST':
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="example.pdf"'
doc = SimpleDocTemplate("example.pdf", pagesize=letter, rightMargin=70, leftMargin=70, topMargin=70,
bottomMargin=60)
report = []
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name="Times", fontName='Times-Roman', fontSize=15, alignment=TA_JUSTIFY))
p_text = "<u>ANEXA 1</u>"
report.append(Paragraph(p_text, styles["Times"]))
report.append(Spacer(1, 5))
doc.build(report)
return response
return render(request, 'pdf_test.html')
pdf_test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Download pdf</title>
</head>
<body>
<form method="POST">
{% csrf_token %}
<button type="submit">Download</button>
</form>
</body>
</html>
What seems to be the problem?
You have to write the file to stream. Try this:
from io import BytesIO
def genereaza_pdf(request):
if request.method == 'POST':
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="example.pdf"'
buff = BytesIO()
doc = SimpleDocTemplate(buff, pagesize=letter, rightMargin=70, leftMargin=70, topMargin=70,
bottomMargin=60)
report = []
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name="Times", fontName='Times-Roman', fontSize=15, alignment=TA_JUSTIFY))
p_text = "<u>ANEXA 1</u>"
report.append(Paragraph(p_text, styles["Times"]))
report.append(Spacer(1, 5))
doc.build(report)
response.write(buff.getvalue())
buff.close()
return response
return render(request, 'pdf_test.html')
The function was generating two files, one in Downloads as default but it was an empty txt and one in the project's directory. I've made the changes.
def genereaza_pdf(request):
if request.method == 'POST':
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="example.pdf"'
doc = SimpleDocTemplate(response, pagesize=letter, rightMargin=70, leftMargin=70, topMargin=70,
bottomMargin=60)
report = []
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name="Times", fontName='Times-Roman', fontSize=15, alignment=TA_JUSTIFY))
p_text = "<u>ANEXA 1</u>"
report.append(Paragraph(p_text, styles["Times"]))
report.append(Spacer(1, 5))
doc.build(report)
return response
return render(request, 'pdf_test.html')
Hi i am using Pandas and displaying a table.
I there a function to apply alternate row color to make it clear to read.
Using below code I am sending table in mail and it works.
my code:
count = 1000
df = pandas.DataFrame.from_dict(result)
df["Total"] = df.T.sum()
html = """<!DOCTYPE html>
<html>
<body>
<h3> %i</h3>
{table_content}
</body>
</html>
""" % count
# Create message container - the correct MIME type is
# multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = " Report"
msg['From'] = sender
msg['To'] = recipients
part2 = MIMEText(html.df(
table_content=df.to_html(na_rep="0")), 'html')
msg.attach(part2)
You can use CSS, namely the tr:nth-child in combination with df.to_html(classes)
Adopt to your case:
from IPython.display import display, HTML
from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
iris = load_iris()
df = pd.DataFrame(data= np.c_[iris['data'], iris['target']],
columns= iris['feature_names'] + ['target'])
HTML('''
<style>
.df tbody tr:nth-child(even) { background-color: lightblue; }
</style>
''' + df.to_html(classes="df"))
Update: Expanding to a specific example
I slightly rearranged the code to allow adding css, as it was conflicting with {} used by .format. You can add your variables to html_variables dict and use %()s to embed them into html. If your html becomes too complicated I recommend looking at using some template engine to make it more robust. Otherwise the code below should be self-explanatory.
html_template = '''<!DOCTYPE html>
<html>
<head>
<style>.df tbody tr:nth-child(even) {background-color: lightblue;}</style>
</head>
<body>
<h3>%(other_var)s</h3>
%(table_content)s
</body>
</html>
'''
html_vars = {'other_var':'IRIS Dataset','table_content':df.to_html(classes="df")}
html = html_template % html_vars
# Create message container - the correct MIME type is
# multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "Report"
msg['From'] = sender
msg['To'] = recipient
part2 = MIMEText(html, 'html')
msg.attach(part2)
Old question, but I found a solution within pandas framework which I put for future use:
def rower(data):
s = data.index % 2 != 0
s = pd.concat([pd.Series(s)] * data.shape[1], axis=1) #6 or the n of cols u have
z = pd.DataFrame(np.where(s, 'background-color:#f2f2f2', ''),
index=data.index, columns=data.columns)
return z
df.style.apply(rower, axis=None)
Essentially the same as #oleg's answer, but rather than the html blocks you can use df.style.set_table_styles like such:
df.style.set_table_styles([{"selector":"tbody tr:nth-child(even)","props":[("background-color","lightgrey")]}])
I find it useful to use the df.style and have my own dark-theme styler. Feel free to use if it is helpful:
def dfdark(styler):
#styler.background_gradient(cmap='coolwarm')
#styler.color('white')
styler.set_table_styles([
{
"selector":"thead",
"props":[("background-color","grey")]
},
{
"selector":"tbody tr:nth-child(even)",
"props":[("background-color","lightgrey")]
},
{
"selector":"th.row_heading",
"props":[("background-color","grey")]
},
{
"selector":"td",
"props":[("border","white")]
},
])
return styler
#styler.format(color='grey')
df=pd.DataFrame(index=np.arange(10),columns=[1,2],data=np.random.normal(size=[10,2]))
df.style.pipe(dfdark)
which outputs:
I want to be able to upload a file to a web application and have each column of the file plotted against the values in the first column. In this particular example, y and error should be plotted against t, yielding two curves.
t
0.0000
1.0000
1.2300
y
1.2345
0.9871
0.5545
error
1.4E-4
-4.9E-3
8.2E-3
I managed to get the upload part working properly but am lost on how I could use the info from a file to plot a curve.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF_8">
<title>Uploader</title>
</head>
<body>
<h1>Upload your file</h1>
<form id = "upload-form" action = "{{ url_for('upload') }}" method = "POST"
enctype = "multipart/form-data">
<input type = "file" name = "file" accept = "file/*" multiple/>
<input type = "submit" value ="upload"/>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Uploaded</title>
</head>
<body>
Uploaded Successfully
</body>
</html>
import os
from flask import Flask,render_template,request
from bokeh.plotting import figure
from bokeh.io import output_file,show
app = Flask(__name__)
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
fg = figure(x_axis_label ="x",y_axis_label = "y")
x = [1,2,3,4]
y = [1,2,3,4]
fg.circle(x,y)
#app.route("/")
def index():
return render_template("view.html")
#app.route("/upload", methods=['POST'])
def upload():
target = os.path.join(APP_ROOT,'files/')
print(target)
if not os.path.isdir(target):
os.mkdir(target)
for file in request.files.getlist("file"):
print(file)
filename = file.filename
destination = "/".join([target,filename])
print(destination)
file.save(destination)
lines = file.read().split('\n') # a list, each element is a line
for line in lines:
x_raw, y_raw, error_raw = line.split(' ') # split the line into three, assign them to variabes
x, y, error = float(x_raw), float(y_raw), float(error_raw) # parse each part into a floating point number
return render_template("uploaded.html")
if __name__ == "__main__":
app.run(debug=True)
You can get the contents of a uploaded file as a string without saving it with file.read()
Read file data without saving it in Flask
Then parse the string. If your file were like this:
0.0000 1.2345 1.4E-4
1.0000 0.9871 -4.9E-3
1.2300 0.5545 8.2E-3
You could use this to extract the values:
lines = file.read().split('\n') # a list, each element is a line
for line in line:
x_raw, y_raw, error_raw = line.split(' ') # split the line into three, assign them to variabes
x, y, error = float(x_raw), float(y_raw), float(error_raw) # parse each part into a floating point number
string.split() works like:
>>> "a,b,c;d,e,f".split(';')
['a,b,c', 'd,e,f']
I want to show OpenCV processed image with web interface (made with CherryPy). Code below works fine:
import cherrypy
import cv2
class Picture(object):
def __init__(self):
self.cam = cv2.VideoCapture(0)
#cherrypy.expose
def index(self):
_, image = self.cam.read()
_, data = cv2.imencode('.jpg', image)
cherrypy.response.headers['Content-Type'] = 'image/jpeg'
return data.tostring()
if __name__ == '__main__':
cherrypy.config.update({'server.socket_host': '127.0.0.1', 'server.socket_port': 80, })
cherrypy.quickstart(Picture())
However: I would like to embed the image in html so I can (for example) add another image or other data to the same page.
I have tried the following code:
import cherrypy
import cv2
class Picture(object):
def __init__(self):
self.cam = cv2.VideoCapture(0)
#cherrypy.expose
def index(self):
_, image = self.cam.read()
_, data = cv2.imencode('.jpeg', image)
return """ <html>
<head>
<title>CherryPy static imagl</title>
</head>
<html>
<body>
<img src=" """ + data + """:image/jpeg" />
</body>
</html>"""
if __name__ == '__main__':
cherrypy.config.update({'server.socket_host': '127.0.0.1', 'server.socket_port': 80, })
cherrypy.quickstart(Picture())
But this gives the following error:
<img src=" """ + data + """:image/jpeg" />
TypeError: cannot concatenate 'str' and 'numpy.ndarray' objects
Converting the numpy arry to a string using the following code also does not work (it gives no error but displays only characters):
<img src=" """ + data.tostring() + """:image/jpeg" />
Anyone who can give me some more insight? Thanks in advance!
The following code does the trick :)
import cherrypy
import cv2
import base64
class Picture(object):
def __init__(self):
self.cam = cv2.VideoCapture(0)
#cherrypy.expose
def index(self):
_, image = self.cam.read()
_, data = cv2.imencode('.jpg', image)
jpeg_base64 = base64.b64encode(data.tostring())
return """
<html>
<head>
<meta http-equiv="refresh" content="1" />
<title>Cherrypy webcam</title>
</head>
<html>
<body>
<img src='data:image/jpeg;base64,%s' />
<img src='data:image/jpeg;base64,%s' />
</body>
</html>
""" % (jpeg_base64, jpeg_base64)
if __name__ == '__main__':
cherrypy.config.update({'server.socket_host': '127.0.0.1', 'server.socket_port': 80, })
cherrypy.quickstart(Picture())
This code displays the same picture two times. and the:
<meta http-equiv="refresh" content="1" />
refreshes the code every second.