Flask 'render_template' after client POST request - javascript

So, I'm working on a small web application that has a small canvas, the user is supposed to draw something and then I want to do some python with the image from that canvas. Like this:
This is working fine. When I press "Click me!", I call a JS function that POST the image to my Flask server. And this is also working, the thing is that after I receive the image, I want to render a new page to show some results, but this page is not rendering at all.
Now, I'm completely new to Javascript, barely know anything, basically every JS I'm using is copy pasted from the internet, so I'm assuming I must be missing something very simple, I just don't know what.
Here is the server code:
from flask import Flask, render_template, request
import re
from io import BytesIO
import base64
from PIL import Image
app = Flask(__name__)
#app.route("/")
def hello_world():
return render_template('drawing.html')
#app.route("/hook", methods=['POST', 'GET'])
def hook():
image_data = re.sub('^data:image/.+;base64,', '', request.form['imageBase64'])
im = Image.open(BytesIO(base64.b64decode(image_data)))
im.show()
return render_template('results.html')
The first route just opens the canvas, the second is executed after the client's request, which has this function:
function surprise() {
var dataURL = canvas.toDataURL();
$.ajax({
type: "POST",
url: "http://127.0.0.1:5000/hook",
data:{
imageBase64: dataURL
}
}).done(function() {
console.log('sent');
});}
Apparently, this is working. I have the line:
im.show()
Just so I can see the image and I get exactly the drawing from the canvas, which I'm now supposed to work on:
but the results page is not rendered afterwards, nothing happens. Why?

The problem is that you are returning the page to view in response to the call that posts the image. Instead, you should return a response (for example in a json format) containing the information regarding the result of the call just made (i.e. the post of the image) and consequently, on the client side, you must redirect the user to the appropriate page .
To understand why it is not working, print the content of the response returned from the call made
var response = '';
$.ajax({ type: "POST",
url: "http://127.0.0.1:5000/hook",
data:{
imageBase64: dataURL
},
success : function(text)
{
response = text;
}
});
alert(response);

Related

Sending image from flask to javascript

I am trying to send an image from the flask server to the web script. The first server connects to another API and gets an image. I don't want to save this image just forward it to the web application script.
#app.route('/api/image', methods=['GET'])
def return_image():
r = requests.get(f"{api_url}/image")
return send_file(
BytesIO(r.content),
mimetype='image/jpeg',
as_attachment=True,
attachment_filename='test.jpg')
I am trying to get this image by ajax request and display it.
function updateImage() {
$.ajax({
url: "/api/image",
type: "GET",
dataType: 'image/jpg',
success: function (res) {
$(theImg).attr("src", 'data:image/png;base64,'+ res);
M.toast({
html: 'Loading image: Success'
})
},
error: function () {
M.toast({
html: 'Loading image: Fail'
})
}
});
}
I tried to make this work but wasn't able to. I really appreciate your help.
At a glance your JS writes a data-uri to the src attribute, but res is actually the binary data with a image/png mimetype.
So you either need to base64 encode r.content on the server side, here's an example which actually creates the entire uri server side, then return that string and have your JS add that to the src attribute.
Or if you just want make your JS support the exisitng endpoint you could probably create a blob based on /api/image response, then write that to the img tag.

Download file from server with Flask and JS

I am trying to download a file when a user clicks on a particular button. This file is an image which gets created when the said button is pressed. What I want is, it should automatically download the image on the client's device.
I am using Flask on the server code, and ideally, the send_file function of Flask should trigger this auto download as it adds the Content-Disposition header.
On the client side, I have a JS code which uses fetch API to send a POST request to the server with some data, which is used for generating the image which is to be downloaded.
This is the JS code:
function make_image(text){
const json={
text: text
};
const options={
method: "POST",
body: JSON.stringify(json),
headers:{
'Content-Type':'application/json',
}
};
fetch('/image',options)
.then(res=>{
res.json(); //Gives an error: Uncaught (in promise) SyntaxError: Unexpected token � in JSON at position 0
}).catch(err=>console.log(err));
}
And this is the Python code on the server:
#app.route('/image',methods=['POST'])
def generate_image():
cont = request.get_json()
t=cont['text']
print(cont['text'])
name = pic.create_image(t)
time.sleep(2)
return send_file(f"{name}.png",as_attachment=True,mimetype="image/png")
But nothing is happening. The image doesnt get downloaded. However,the image is getting created on the server and is not corrupt
How do I resolve this? And is there some other way to do what I am trying to do?
You can do the below
return send_from_directory(dir, file_name, as_attachment=True)
This will download the file on the user's machine.
Edit:
BTW, if you create an html form like below, you do not need javascript.
<form action='action here' method='post'>
<input type='submit'>
</form>
As #clockwatcher mentioned a different question, I used the download.js module to handle the download of the image.
So my JS code looks like this now:
function make_image(text){
const json={
text: text
};
const options={
method: "POST",
body: JSON.stringify(json),
headers:{
'Content-Type':'application/json',
}
};
fetch('/image',options)
.then(res=>{
return res.blob();
}).then(blob=>{
download(blob)
}).catch(err=>console.log(err));
}
And an addition to the script tag in the html:
<script src="https://cdnjs.cloudflare.com/ajax/libs/downloadjs/1.4.8/download.min.js"></script>
With no change in the Python server code.
It works now

AXIOS POST request + render_template FLASK

I have a page with a ton of input boxes (numbers, checkboxes etc.), I need to prepare the data before the POST request. So I have a bunch of methods that make adjustments to the names of these inputs, bundle them up nicely in one object called: data. For my purposes, putting the submit button in the above form does not work, because the POST request is done with the default names. For that reason all the above inputs are enclosed in a div, and then I have a submit button and onClick I am doing an axios POST request:
axios({
method: 'post',
url: '/smart-beta/',
data
});
On the Flask end I have this:
elif request.method == "POST":
sbe_args = json.loads(request.data)
sb = SbeGenerator(sbe_args)
sb.run() # TODO: depending on how long this takes, may add a loading animation to the page
eg = ExcelGenerator(sb)
eg.create_excel()
beta = sb.beta
sharpe = sb.sharpe
annualised_return = sb.ann_mean
annualised_vol = sb.ann_vol
time_series = sb.port_json
stocks = sb.last_stocks_json
print("Time series: " + str(time_series))
print("stocks: " + str(stocks))
# TODO: Do the redirect here (getting 302 status on the POST request if redirect is here, becasue
# TODO: axios catches it in then
print("Rendering template")
return render_template('smart_beta/calculation.html')
I get the 200 on the POST request, but it never renders my other page. It just hangs on the current page. Is that because the axios's promise is never resolved? when I do .then(function(response) {console.log(response);}); <- it still hangs. The reason I need to use render_template is because I need to pass stuff like time_series into that page.
EDIT: When I navigate to the response of the POST request in the dev tools on the web page, I can see that it is the page that I need.

Flask Ajax return multiple statuses before execution

I've got a web front-end for a flask app that is doing some heavy calculations on the back-end before passing the calculated data via JSON to the front-end to get charted.
The issue is, the calculation takes several minutes, and ideally I want the front-end to know the % done and status of the request rather than sitting there quietly.
JS Ajax request front-end:
function runBacktest() {
$.ajax({
type: "POST",
url: '/backtest',
data: {
run_partial: 1,
use_saved_model: false,
saved_model_file: 'model.h5'
},
dataType: "json",
success: function(data) {
//Successful data
},
error: function () {
alert('There was a problem contacting the server.');
}
});
}
Python backend:
#webapp.route('/backtest', methods=['POST'])
def backtest():
print('> Running Backtest...')
"""Calculation code goes on here"""
print('Percent Completed: \r{0:.1f}%'.format(round(pct_done,1)), end='')
"""Serialise each agent instance into a dictionary and put in list to be converted to JSON object"""
agents_serialised = [agent.__dict__ for agent in bt_engine.agents]
return json.dumps(agents_serialised)
Question is, how can I pass something like the percentage done, which I'm printing on stdout, on every change of percentage, to the front end? Followed by then passing the json data once it's finished?
A more Flasky approach would be to pass a generator to the response. From what I've read, this is the preferred approach to streaming data with Flask. Here's a very abstract example. Look here for my answer to different question where I have a more fleshed out and tested script using a generator in a response.
def do_calcs():
while(more_calculations):
"""do your calculations and figure out the percentage."""
agents_serialized = [agent.__dict__ for agent in bt_engine.agents]
yield json.dumps({percent: percent, agents: agents_serialized})
And in your route:
return Response(do_calcs(), mimetype='text/json')
For updating the percentage kind of stuff we need socket connection between Front end and Back end .Flask have a great package do this which is socket.io. And there is javascript support also for the socket.io.
Blog post which help to build this
I am sure you can build with this, because i done it before.
Sample Python code:
from flask import Flask, render_template
from flask.ext.socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#app.route('/')
def index():
return render_template('index.html')
#socketio.on('my percentage event', namespace='/test')
def test_message(message):
#create pertage opertion here
emit('percentage status', {'data': message['data']}, broadcast=True)
Sample javascript code:
$(document).ready(function(){
var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
socket.on('percentage status', function(msg) {
//portion for update the percentage
});
});
These code are not exactly, But you can use as reference.

JSON passed from Python (Flask) into JavaScript is displaying on screen

I am passing a JSON from a Python back-end into my front-end JavaScript where I'm running a webGL (three.js) animation. The JSON holds numerical values that determine what happens in the animation. My problem is that while I have a basic ajax request working, the JSON is being printed to the screen (in lieu of the animation) rather than becoming a variable I can iterate through to control aspects of the animation. The two halves of the call are shown below.
I asked a related question to this one before and got some great help, but am obviously still missing a piece of the puzzle. I've been reading docs and all sorts of sources, yet need a nudge in the right direction to finally get this working. Any help is appreciated!
In the python backend:
from flask import Response, json, render_template, jsonify
from app import app
from motifs import get_motif, get_motif_list
#app.route('/')
def index():
motifs = get_motif_list(10)
# The first version of the return below successfully sends data, yet it is printed to the
# screen, rather than being stored as data in a variable.
return Response(json.dumps(motifs), mimetype='application/json')
# This version of the return does not work:
# return render_template("index.html", motifs = motifs)
In the JavaScript (note that the console.log sanity checks don't work - I have no idea why:
function foo() {
var array_data;
$.ajax({
type: "GET",
url: "/",
dataType: "json"
});
request.done(function(JSON_array) {
array_data = JSON.parse(JSON_array)["array"]
console.log(array_data); // sanity check - doesn't work
});
return array_data;
};
var array = foo();
console.log(array); // sanity check - doesn't work
UPDATE
With help from the advice below, I'm pretty close to having this off the ground. The JSON is no longer printing to the screen (an issue caused by the Flask return), and I've solved a multifunction callback issue I discovered along the way. However, I am now getting a parsererror from the complete textStatus. I think the problem now lays in the Python/Flask (see current code below). Thanks again for all who've helped!
Python/Flask (I think the problem is here - I'm a noob to Flask):
from flask import Response, json, render_template, jsonify
from app import app
from motifs import get_motif, get_motif_list
#app.route('/')
def index():
motifs = get_motif_list(10)
return Response(json.dumps(motifs), mimetype='application/json')
#app.route("/")
def index():
return render_template("index.html")
The JavaScript (the data is returned by the Deferred object - used to solve a callback issue):
function getData() {
var deferredData = new jQuery.Deferred();
$.ajax({
type: "GET",
url: "/",
dataType: "json",
success: deferredData.resolve(),
complete : function(xhr, textStatus) {
console.log("AJAX REquest complete -> ", xhr, " -> ", textStatus)}
});
return deferredData; // contains the passed data
};
It turns out I had a lot of problems in my code above, several of which I had to debug in related questions here and here.
Among them were:
in my original Flask index() function, it was dumping the JSON data to the screen because I was not rendering the index.html template anywhere.
I had matching routes ('/') and function names (index()) in the Flask functions
As mentioned in the comments I did an unnecessary double parsing of the JSON with dataType: json and array_data = JSON.parse(JSON_array)
the return from this asynchonous function always came up undefined because it was referenced before the call had resolved
in my later update to a Deferred object, the success property should have read: success: function(data) { deferredData.resolve(data);}
So, after all those fixes, here is the functioning code!
Flask/Python:
from flask import Response, json, render_template, jsonify
from app import app
from motifs import get_motif, get_motif_list
#app.route('/ajax')
def ajax() :
motifs = get_motif_list(10)
return Response(json.dumps(motifs), mimetype='application/json')
#app.route("/")
def index():
return render_template("index.html")
JavaScript: (note: this is the foo() function in my question above)
function getData() {
var deferredData = new jQuery.Deferred();
$.ajax({
type: "GET",
url: "/ajax",
dataType: "json",
success: function(data) {
deferredData.resolve(data);
},
complete: function(xhr, textStatus) {
console.log("AJAX Request complete -> ", xhr, " -> ", textStatus);
}
});
return deferredData; // contains the passed data
};
// I used the Deferred structure below because I later added Deferred objects from other asynchronous functions to the `.when`
var dataDeferred = getData();
$.when( dataDeferred ).done( function( data ) {
console.log("The data is: " + data);
});
In your javascript:
function foo() {
var array_data;
$.ajax({
type: "GET",
url: "/",
dataType: "json"
});
request.done(function(JSON_array) {
/*JSON_array is already parsed and it is an object..*/
array_data = JSON.parse(JSON_array)["array"]
/* ^ I belive you dont have to do this ^*/
console.log(array_data);
/* ^ change this to console.log(JSON_array);*/
});
return array_data;
/* ^ change this to return JSON_array;*/
};

Categories

Resources