Flask Serve PIL image in HTML from endpoint - python

I will preface by saying this is my first time working with flask & HTML to build a web app, so I may be using the wrong terminology in some places, apologies in advance.
I have used these two previous questions as reference for what i'm doing:
Flask render a word cloud
Passing a matplotlib figure to HTML (flask)
I am working a web app that allows a user to input a movie, and a wordcloud is returned.
The user starts on /search, where they input a movie name, I then redirect to /search_results where a list of movies with similar names are shown, the user selects the right film and submits. This part of the journey is all working fine, from here I take the movie name, I then apply a function I have built in python that creates a wordcloud based on this film name (in my code below this is what the function wordcloud_generator(1,session['search_value']) is doing in the fig route). The output of the wordcloud_generator() function is:
...
img = BytesIO()
wordcloud.to_image().save(img, 'PNG')
img.seek(0)
return img
I want to save this image to a route "/fig/<wordcloud_img>" and then be able to call it in the src of an img tag in the route "/images/<wordcloud_img>".
When running through this I get the error of at the point that I submit the movie name from /search_results and redirect to 'images'. Error: werkzeug.routing.BuildError: Could not build url for endpoint 'images'. Did you forget to specify values ['wordcloud_img']?
After this if I navigate manually to "localhost:5000/fig/wordcloud_img" then my function seems to run and the image is shown, and then if I manually navigate to "localhost:5000/images/wordcloud_img" the image is properly surfaced in the html.
It seems like I am doing this in the wrong order somehow and the function isn't running/generating the image before I try to access it on the /images source.
Routes
def search():
if request.method == 'POST':
movie_search = request.form['search_text']
session['returned_movies'], session['search_links'] = search_function(search_term = movie_search)
return redirect(url_for('search_result'))
return render_template('search.html',title='Search')
#app.route("/search_result", methods=['GET', 'POST'])
def search_result():
if request.method == 'POST':
movie = request.form['movie']
session['search_value'] = session['search_links'][session['returned_movies'].index(movie)]
return redirect(url_for('images'))
return render_template('search_results.html',title='Search Results')
#app.route("/images/<wordcloud_img>")
def images(wordcloud_img):
return render_template("wordcloud.html")
#app.route("/fig/<wordcloud_img>")
def fig(wordcloud_img):
img = wordcloud_generator(1,session['search_value'])
return send_file(img, mimetype='image/png')
wordcloud.html
{% extends "layout.html" %}
{% block content %}
<div>
{% if session['search_value'] %}
<p>Searching for a movie: {{ session['search_value'] }}</p>
{% else %}
<p>Oops, no movie selected </p>
{% endif %}
<body>
<img src="{{ url_for('fig', wordcloud_img = 'wordcloud_img') }}" alt="Image Placeholder" height="100">
</body>
</div>
{% endblock content %}

The problem is that you are not passing the image to the 'images' function that displays the template with the image. In your 'search_result' function, you will need to pass the wordcloud_image to the 'images' function like so:
return redirect(url_for('images'), wordcloud_img=your_image)
(replacing your_image with the actual image variable)
In your images route, you would then need to pass this received image to the template:
return render_template("wordcloud.html", wordcloud_img=wordcloud_img)
Then in your template, you can use this wordcloud_img variable like this:
<img src="{{ url_for('fig', wordcloud_img=wordcloud_img) }}" alt="Image Placeholder" height="100">

Related

How to display pictures from GridFS to web browser?

This is how I wrote the gallery function which is supposed to display images stored in MongoDB using GridFS:
#app.route('/gallery/<username>')
def gallery(username):
user = db.users.find_one({'username': username})
images = user['images']
for img in images:
image = grid_fs.get(img)
base64_data = codecs.encode(image.read(), 'base64')
image = base64_data.decode('utf-8')
return render_template('gallery.html')
How am I supposed to write the HTML document in order for the photos to be displayed?
First, you need to return data to template:
return render_template('gallery.html', **images)
And then you can use it in your template like this:
{% for img in images %}
<img src="data:image/png;base64, {{ img }}">
{% endfor %}

Pass html form input as argument for python function (recommender system)

I have built a recommender system using a Keras CNN. I am deploying it with Flask as a web application. Currently it displays images in the form of a gallery. I want to use html form to click the image and then generate recommendations from the image clicked.
I am using PIL to pass into the model. So far everything works fine, I'm a bit stuck on how to make it pass into the function and return the results. Thank you! I'm new and this is my first go around with Flask.
Tried several ways to pass the form, however not sure if I have been doing it the correct way.
Also have tried several variations on the function.
I want to pass the image clicked into my python function, then return back the related images. So far I have this for index.html:
<form action="{{ url_for('predict')}}" method="POST">
<div class="gallery">
{% for image in images %}
<button type="submit" class="btn btn-primary"> <img
src="static/img/{{ image }}"></button>
{% endfor %}
</div>
</form>
#Here is my recommender function:
#app.route('/predict', methods=['POST'])
def predict():
if request.method == 'POST':
product = request.form['product']
# compute cosine similarities between images
cosine = cosine_similarity(imgs_features)
# creates a dataframe of the cosine similarites
cosine_df = pd.DataFrame(cosine, columns=products,
index=products)
sim_images = 5
p_name = os.path.basename(str(product))
image = load_img(product, target_size=(img_size, img_size))
plt.imshow(image)
plt.show()
recommend_imgs = cosine_df[product].sort_values(ascending=False)
[1:sim_images + 1].index
for i in range(0, len(recommend_imgs)):
image = load_img(recommend_imgs[i], target_size=(img_size,
img_size))
# plt.imshow(image)
# plt.show()
rec_name = os.path.basename(str(recommend_imgs[i]))
return render_template('results.html', name=product,
res=rec_name, pred_imgs=plt.imshow(image))
I get this error mostly:
werkzeug.exceptions.HTTPException.wrap..newcls: 400 Bad Request: KeyError: 'product'
Along with:
return self.wsgi_app(environ, start_response)
This is one way to start the function. I am not sure how to pass the image value that you need to the form, unless each image as a specific value name you can pass it to the function.
Html:
{% for image in images %}
<form action="{{ url_for('predict')}}" method="POST">
<button type="submit" name="submit" value="{{ image.value}}">
<img src="static/img/{{ image }}">
</button>
</form>
{% endfor %}
python:
#app.route('/predict', methods=['POST'])
def predict():
if request.method == 'POST':
if request.form['submit'] == '<image.value_1>':
# do something
...
elif request.form['submit'] == '<image.value_2>':
# do something
...
...
well, that's all.

Submitting contents of file to a Django view in HTML

I'm working with Django v1.4 (Python v.2.7.3) and I'm trying to build a proofchecking application. My proofchecker has an "Examples" page with a list of links to example proofs for the user, and these are rendered on the screen one after the other. A small sample is shown below:
These example files are saved at MEDIA_ROOT on the server, and what I want is a way to make it so that clicking the link will pass the contents of the file in a POST message to a particular view. That view already has code designed to handle the user uploading a proof file directly from their file system, so essentially what I want to do is make it so that the examples.html template (shown below) passes the same kind of information except for files already stored on the server.
The code for examples.html is:
{% load staticfiles %}
<html>
<head>
<title>Anaconda</title>
<style>
body
{
overflow-y: scroll;
}
</style>
</head>
<body>
<center>
<img src="{% static "AnacondaTitleText.png" %}" alt="Title" height="40%" width="40%"/>
<div align="left" style="width:800px;">
<h2>Anaconda Examples</h2>
<p>You can click the button beside each example on this page to load it into the proof window.
{% if examples %}
The following examples are included with Anaconda:</p>
<br>
{% for example in examples %}
<p>{{ example.exampleFile.name|cut:"./" }}: link</p>
<br>
{% endfor %}
{% else %}
There are no examples currently included with Anaconda.</p>
{% endif %}
</div>
</center>
</body>
</html>
The "a href..." part will need to be changed because currently, clicking it will bring up a "Save file" dialog which is not what I want.
In my server's views.py file, I have the following code capable of handling uploaded files:
def proof(request):
if request.method == 'POST':
defaultText = request.FILES['docfile'].read()
proofText = ProofForm(request.POST)
else:
defaultText = ""
proofText = ProofForm()
c = {}
c.update(csrf(request))
c['form'] = proofText
c['default_text'] = defaultText
return render_to_response('server/proof.html', c)
I suppose what I want is a way to do the following:
The user clicks the link next to a particular example proof
All the necessary information gets loaded into request.FILES
This gets sent to the server as a POST request to proof(request)
proof(request) treats it like a regular uploaded file and reads the file contents
My models.py file looks like this:
from django.db import models
class Example(models.Model):
exampleFile = models.FileField(upload_to='.')
I'd be happy to provide additional information if necessary.
I found a solution to this, but I suspect it's a bit hacky.
Inside examples.html I include the following:
{% for example in examples %}
<form name="example" action="/server/proof" method="post">
{% csrf_token %}
<p>{{ example.exampleFile.name|cut:"./" }}</p>
<input type="hidden" name="example" value="{{ example.exampleFile.name|cut:"./" }}">
<input type="submit" value="Select">
</form>
<br>
{% endfor %}
I associate the name of the file with a hidden input element, and then in views.py I have the following code:
def proof(request):
if request.method == 'POST':
print "DATA"
print str(request.POST)
try:
defaultText = request.FILES['docfile'].read()
except:
examplePath = request.POST.get('example')
with open(join(MEDIA_DIR, examplePath), 'r') as f:
defaultText = f.read()
proofText = ProofForm(request.POST)
else:
defaultText = ""
proofText = ProofForm()
c = {}
c.update(csrf(request))
c['form'] = proofText
c['default_text'] = defaultText
return render_to_response('server/proof.html', c)
It's patchwork, but it does what I want. Hopefully I'll have time to improve the code at some later time.

How to read images from directory, assemble them in <img src="">, and have flask return image?

I have a directory full of images. In one of my templates, I want to display these images
GET template:
render_template("registration.html", images=images)
in template: (using image/{{image}} because url_for doesn't accept variables)
{% for image in images %}
<input class="avatar" type = "radio" name = "avatar" value = "{{image}}"/><img src = "image/{{image}}"/>
{% endfor %}
and in app.py
#app.route('/images/<image>')
def images(image):
return send_file('public/assets/thumbs'+image, mimetype='image/jpg')
(public/assets/thumbs is the directory images reside in) however, this fails completely. Is there any tricks or hacks I can use to make this work?
I don't know if it could help you neither if the method i'll describe is good practice but to display images from directory (using send_from_directory in my view as described below by Andrew Allbright) in templates I was using :
{% for image in images %}
<img src="{{url_for('static', filename='image/')}}{{image}}"></img>
{% endfor %}
I remember I took inspiration from this SO post: Create dynamic URLs in Flask with url_for() (second answer) that indicates you can pass argument to url_for() in your templates.
Solution: send_from_directory()
#app.route("/image/<image>", methods=["GET"])
def image(image):
return send_from_directory(app.static_folder, 'assets/thumbs/' + image, mimetype='image/jpg')

how to render only part of html with data using django

I am using ajax to sort the data which came from search results.
Now I am wondering whether it is possible to render just some part of html so that i can load this way:
$('#result').html(' ').load('/sort/?sortid=' + sortid);
I am doing this but I am getting the whole html page as response and it is appending the whole html page to the existing page which is terrible.
this is my views.py
def sort(request):
sortid = request.GET.get('sortid')
ratings = Bewertung.objects.order_by(sortid)
locations = Location.objects.filter(locations_bewertung__in=ratings)
return render_to_response('result-page.html',{'locs':locations},context_instance=RequestContext(request))
how can I render only that <div id="result"> </div> from my view function? or what am I doing here wrong?
From what I understand you want to treat the same view in a different way if you receive an ajax request.
I would suggest splitting your result-page.html into two templates, one that contains only the div that you want, and one that contains everything else and includes the other template (see django's include tag).
In your view then you can do something like the following:
def sort(request):
sortid = request.GET.get('sortid')
ratings = Bewertung.objects.order_by(sortid)
locations = Location.objects.filter(locations_bewertung__in=ratings)
if request.is_ajax():
template = 'partial-results.html'
else:
template = 'result-page.html'
return render_to_response(template, {'locs':locations},context_instance=RequestContext(request))
results-page.html:
<html>
<div> blah blah</div>
<div id="results">
{% include "partial-results.html" %}
</div>
<div> some more stuff </div>
</html>
partial-results.html:
{% for location in locs %}
{{ location }}
{% endfor %}

Categories

Resources