Small File Upload with Flask is Slow - javascript

I'm uploading small files (sub 20k) using Fetch and Flask, however it can take up to 10 seconds to post, process and then return. Conversely, if i run the same functions in pure python only the same load and process time is less than a second.
When running a file upload with no processing it is almost instant upload.
Am I missing something that slows things down when Im processing files in with Flask?
I've tried uploading with no processing (fast), I've tried processsing with no flask (fast). I've tried uplaoding and processing from browser with flask(slow)
from flask import render_template, url_for, flash, redirect, request, jsonify, Response, Flask, session, make_response, Markup
import io, random, sys, os, pandas
app = Flask(__name__)
#app.route("/")
############################################################
#app.route("/routee", methods = ['GET', 'POST'])
def routee():
return render_template('Upload Test.html')
############################################################
#app.route("/routee/appendroute", methods = ['GET', 'POST'])
def appendroute():
PrintFlask(request.files.getlist('route'))
return make_response(jsonify('Voyage = VoyageJson, Intersects = IntersectJson'), 200)
############################################################
if __name__ == "__main__":
app.run(debug=True)
<script type="text/javascript">
function prepformdata(route){
formdata = new FormData();
for (var i = 0; i < route.files.length; i++) {
formdata.append('route', route.files[i]);
}
return formdata
}
//////////////////////////////////////////////////////////////
function appendroutes(formdata, route) {
uploadfiles = document.getElementById("files")
formdata = prepformdata(uploadfiles)
InitConst ={method: "POST",body: formdata}
url = window.origin + '/routee/appendroute'
fetch(url,InitConst)
.then(res => res.json())
.then(data => {addon = data['Voyage']; addonIntersects = data['Intersects']})
.then(() => console.log('Route(s) Added'))
}
</script>
Whilst the code above is very nippy. I'm expecting to do some equally nippy processing server side. But something is slowing it down. Any ideas why processing might slow down when flask is used?

Related

websocket only sends one message?

I have a small server in Python that I'm trying to get to terminate based on a message it gets from a client. This is the server:
async def receiver(websocket, path):
received_data = await websocket.recv()
print("< {}".format(received_data))
if received_data == "Order66":
websocket.keep_running = False
asyncio.get_event_loop().stop()
else:
print("< {}".format(received_data))
def start_the_server():
start_server = websockets.serve(receiver, 'localhost', 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
def main():
start_the_server()
if __name__ == "__main__":
main()
The "client" is a larger app which also has a Javascript API and I am trying to this:
// send the info and disconnect
const socket2 = new WebSocket('ws://localhost:8765');
socket2.addEventListener('open', function (event) {
socket2.send(stats);
socket2.send("Order66");
});
However, it only ever sends "stats" (in this case it's a string), and the second send(...) never seems to get sent.

How to dynamically update context values in render_template() via GET requests?

I have a framework that retrieves the values of a slider via POST requests from HTML to Flask using AJAX. This calls the /about endpoint to perform some data manipulation and generates a redirected link defining the API url. Another endpoint (/index) then takes that API url and retrieves a response from the data source. However, when I attempt to render the template under the /index endpoint, I am unable to observe a dynamically rendering template suited for further upstream data visualization tasks.
Here's a very basic example illustrating the pipeline I've laid out:
./templates/index.html:
<body>
<div id="map" style="height: 700px;"></div>
<form method = 'POST'>
<div class="rangeslider">
<input style="width: 50%;" type="range" min="0" max="64" value="64" class="myslider" id="sliderRange">
<p>
<span id="demo"></span>
</p>
</div>
<h1><b>Testing context value: {{ asdf }}</b></h1>
<script>
const Http = new XMLHttpRequest();
var rangeslider = document.getElementById("sliderRange");
var output = document.getElementById("demo");
var current;
rangeslider.oninput = function() {
// Step 1: receives value from HTML slider object
current = this.value;
Http.open('POST', '/about')
Http.send(current)
}
</script>
</body>
./app.py:
from flask import Flask, render_template, request, redirect, url_for, jsonify
app = Flask(__name__)
app.config['TEMPLATES_AUTO_RELOAD'] = True
#app.route('/')
def main():
return redirect('/index')
#app.route('/index', methods=['GET'])
def index():
asdf = request.args.get('asdf')
// Step 3: asdf undergoes request to pull in appropriate data from data source and would ideally generate custom HTML prepped to inject a child into an existing leaflet.js app
// Issue occurs in asdf = asdf statement. Ideally asdf will represent custom HTML dependent on slider value.
return render_template('index.html', asdf = asdf)
#app.route('/about', methods=['POST'])
def about():
received_data = request.data
// Step 2: received_data undergoes further preprocessing to create API URL
return redirect(url_for("index", asdf=str(received_data)))
if __name__ == '__main__':
app.run(port=8000)
I know that AJAX is typically the best solution to deal with dynamic contexts, but I am unsure how to make the appropriate fixes to suit my overall needs in this case.
Any assistance would be greatly appreciated. Thanks!
This is how you can pass parameters to route:
from flask import Flask, render_template, request, redirect, url_for, jsonify
app = Flask(__name__)
app.config['TEMPLATES_AUTO_RELOAD'] = True
#app.route('/')
def main():
return redirect('/index')
#app.route('/index/<asdf>', methods=['GET'])
def index(asdf):
// Step 3: asdf undergoes request to pull in appropriate data from data source and would ideally generate custom HTML prepped to inject a child into an existing leaflet.js app
// Issue occurs in asdf = asdf statement. Ideally asdf will represent custom HTML dependent on slider value.
return render_template('index.html', asdf = asdf)
#app.route('/about', methods=['POST'])
def about():
received_data = request.data
// Step 2: received_data undergoes further preprocessing to create API URL
return redirect(url_for("index", asdf=str(received_data)))
if __name__ == '__main__':
app.run(port=8000)
Adding to optimising your code, you don't need to pass asdf to index and reload the html. You can pass asdf as a response from /about to your js and insert it into the html. This might help you.
In your app.py
#app.route('/about', methods=['POST'])
def about():
received_data = request.data
data = {'status':'success', 'xyz':received_data}
resp = make_response(jsonify(data), 200)
return resp
You may then access this response in the XMLHttp request as:
<script>
const Http = new XMLHttpRequest();
var rangeslider = document.getElementById("sliderRange");
var output = document.getElementById("demo");
var current;
rangeslider.oninput = function() {
// Step 1: receives value from HTML slider object
current = this.value;
Http.open('POST', '/about')
Http.send(current)
}
Http.onload = function() {
var response = JSON.parse(Http.responseText)
if (response['status'] == "success") {
$('#tag').html(response['xyz'])
}
}
</script>
You may change how you use the response to your context.

Flask only working as expected in debug mode

I'm creating a desktop application using electron and flask. When I attempt to send a selected path (selected by the user via a folder select dialog. Using electron I am able to get the full path and not something like C:/fake_path. This is because electron provides the full path via .files[0].path) to python using flask, I never receive a response or caught an error (the main.logPython function is never called in either branch following the rq).
However, if I include app.debug = True in the python file to turn on the Flask debugger, it runs as expected and prints out the path. Essentially the python code is not even being reached unless I put it into debug mode.
edit: I know this is true because I've attempted to run python code other than just the return (like creating a new file) and it's never run.
edit #2: It seems this problem is related to the admin.js file. If I attempt to make a similar connection in the main.js file I get the result that I expect, however if I use the same code in the admin.js file I get no response as stated above.
Hopefully I've provided enough information. If you need anything else please let me know.
Sorry for any formatting issues with the JavaScript code, I'm very new to it so I just threw it into dirty markup to clean it up a bit.
calling JavaScript code (admin.js)
const remote = require("electron").remote;
const main = remote.require("./main.js");
var importButton = document.getElementById("importButton");
if (importButton.addEventListener) {
importButton.addEventListener("click", importPackages, false);
} else if (importButton.attachEvent) {
importButton.attachEvent("onclick", importPackages);
}
function importPackages() {
var importDirectory = document.getElementById("importDirectory").files[0].path;
var rq = require('request-promise');
var mainAddr = 'http://localhost:5000';
rq(mainAddr + "/import_packages/" + importDirectory).then(function(htmlString) {
if (htmlString != "false") {
main.logPython(htmlString);
}
}).catch(function(err) {
main.logPython('request not sent');
})
}
python code (database_handler.py)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, request
import sqlite3
app = Flask(__name__)
# app.debug = True
#app.route("/")
def hello():
return "true"
#app.route("/login/<username>/<password>")
def login(username, password):
conn = sqlite3.connect("hict.sqlite")
cursor = conn.cursor()
cursor.execute("SELECT firstname, lastname, userclass FROM credentials "
"WHERE username=? AND password=? LIMIT 1;",
(username, password))
data = cursor.fetchall()
if len(data) == 0:
return "false"
else:
return "|".join(map(str, data[0]))
#app.route("/import_packages/<path:import_directory>")
def import_packages(import_directory):
return import_directory
if __name__ == "__main__":
app.run(host='127.0.0.1', port=5000)
Main JavaScript code (main.js)
const electron = require("electron");
const {app, BrowserWindow} = electron;
app.on('window-all-closed', () => {
app.quit();
})
app.on('ready', () => {
var subpy = require('child_process').spawn('python', ['./database_handler.py']);
//var subpy = require('child_process').spawn('./dist/hello.exe');
var rq = require('request-promise');
var mainAddr = 'http://localhost:5000';
var openWindow = () => {
let win = new BrowserWindow({
width: 1920,
height: 1080
});
win.maximize();
win.setMenu(null);
win.loadURL(`file://${__dirname}/login.html`);
win.on('closed', () => {
win = null;
subpy.kill('SIGINT');
})
}
var startUp = () => {
rq(mainAddr).then(function(htmlString) {
console.log('server started!');
openWindow();
}).catch(function(err) {
console.log('waiting for the server start...');
startUp();
})
}
startUp();
})
exports.logPython = (returnData) => {
console.log(returnData);
}
It looks like you are using admin.js as code in your UI, so it is in a renderer process. Your main.js looks like it is running in your project's main process. I am unsure if it actually works to require main.js in admin.js for this case. Why not try using ipc to send the data from your UI to the main process (main.js)?
Also, to eliminate an obvious answer...
console.log() in the main process (main.js) will print out in your terminal as you expect. console.log() in the renderer process (admin.js) prints out in the dev console. On windows I think you open that with by clicking on your UI and then ctrl+shift+i. So if you didn't know this, it might be working but you don't see it.

Using the SSE protocol in Python Flask

I am trying to write a realtime web app which can respond real time messages updating in Flask framework. I am using code from http://flask.pocoo.org/snippets/116/ but JavaScript EventSource SSE not firing in browser. From the log I can see I've published the data successfully but the webpage(http:xxxx:5000/) does not get updated at all.
import gevent
from gevent.wsgi import WSGIServer
from gevent.queue import Queue
from flask import Flask, Response
import time
# SSE "protocol" is described here: http://mzl.la/UPFyxY
class ServerSentEvent(object):
def __init__(self, data):
self.data = data
self.event = None
self.id = None
self.desc_map = {
self.data : "data",
self.event : "event",
self.id : "id"
}
def encode(self):
if not self.data:
return ""
lines = ["%s: %s" % (v, k)
for k, v in self.desc_map.iteritems() if k]
return "%s\n\n" % "\n".join(lines)
app = Flask(__name__)
subscriptions = []
# Client code consumes like this.
#app.route("/")
def index():
debug_template = """
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Server sent events</h1>
<div id="event"></div>
<script type="text/javascript">
var eventOutputContainer = document.getElementById("event");
var evtSrc = new EventSource("/subscribe");
evtSrc.onmessage = function(e) {
console.log(e.data);
eventOutputContainer.innerHTML = e.data;
};
</script>
</body>
</html>
"""
return(debug_template)
#app.route("/debug")
def debug():
return "Currently %d subscriptions" % len(subscriptions)
#app.route("/publish")
def publish():
#Dummy data - pick up from request for real data
def notify():
msg = str(time.time())
for sub in subscriptions[:]:
sub.put(msg)
print 'data is ' + str(time.time())
gevent.spawn(notify)
return "OK"
#app.route("/subscribe")
def subscribe():
def gen():
q = Queue()
subscriptions.append(q)
try:
while True:
result = q.get()
ev = ServerSentEvent(str(result))
print 'str(result) is: ' + str(result)
yield ev.encode()
except GeneratorExit: # Or maybe use flask signals
subscriptions.remove(q)
return Response(gen(), mimetype="text/event-stream")
if __name__ == "__main__":
app.debug = True
server = WSGIServer(("", 5001), app)
server.serve_forever()
# Then visit http://localhost:5000 to subscribe
# and send messages by visiting http://localhost:5000/publish
Can you please shed some lights? I am testing with Chrome/34.0.1847.116. Thanks.

How to make a JSON call to an URL?

I'm looking at the following API:
http://wiki.github.com/soundcloud/api/oembed-api
The example they give is
Call:
http://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=json
Response:
{
"html":"<object height=\"81\" ... ",
"user":"Forss",
"permalink":"http:\/\/soundcloud.com\/forss\/flickermood",
"title":"Flickermood",
"type":"rich",
"provider_url":"http:\/\/soundcloud.com",
"description":"From the Soulhack album...",
"version":1.0,
"user_permalink_url":"http:\/\/soundcloud.com\/forss",
"height":81,
"provider_name":"Soundcloud",
"width":0
}
What do I have to do to get this JSON object from just an URL?
It seems they offer a js option for the format parameter, which will return JSONP. You can retrieve JSONP like so:
function getJSONP(url, success) {
var ud = '_' + +new Date,
script = document.createElement('script'),
head = document.getElementsByTagName('head')[0]
|| document.documentElement;
window[ud] = function(data) {
head.removeChild(script);
success && success(data);
};
script.src = url.replace('callback=?', 'callback=' + ud);
head.appendChild(script);
}
getJSONP('http://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=js&callback=?', function(data){
console.log(data);
});
A standard http GET request should do it. Then you can use JSON.parse() to make it into a json object.
function Get(yourUrl){
var Httpreq = new XMLHttpRequest(); // a new request
Httpreq.open("GET",yourUrl,false);
Httpreq.send(null);
return Httpreq.responseText;
}
then
var json_obj = JSON.parse(Get(yourUrl));
console.log("this is the author name: "+json_obj.author_name);
that's basically it
In modern-day JS, you can get your JSON data by calling ES6's fetch() on your URL and then using ES7's async/await to "unpack" the Response object from the fetch to get the JSON data like so:
const getJSON = async url => {
const response = await fetch(url);
if(!response.ok) // check if response worked (no 404 errors etc...)
throw new Error(response.statusText);
const data = response.json(); // get JSON from the response
return data; // returns a promise, which resolves to this data value
}
console.log("Fetching data...");
getJSON("https://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=json").then(data => {
console.log(data);
}).catch(error => {
console.error(error);
});
The above method can be simplified down to a few lines if you ignore the exception/error handling (usually not recommended as this can lead to unwanted errors):
const getJSON = async url => {
const response = await fetch(url);
return response.json(); // get JSON from the response
}
console.log("Fetching data...");
getJSON("https://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=json")
.then(data => console.log(data));
Because the URL isn't on the same domain as your website, you need to use JSONP.
For example: (In jQuery):
$.getJSON(
'http://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=js&callback=?',
function(data) { ... }
);
This works by creating a <script> tag like this one:
<script src="http://soundcloud.com/oembed?url=http%3A//soundcloud.com/forss/flickermood&format=js&callback=someFunction" type="text/javascript"></script>
Their server then emits Javascript that calls someFunction with the data to retrieve.
`someFunction is an internal callback generated by jQuery that then calls your callback.
DickFeynman's answer is a workable solution for any circumstance in which JQuery is not a good fit, or isn't otherwise necessary. As ComFreek notes, this requires setting the CORS headers on the server-side. If it's your service, and you have a handle on the bigger question of security, then that's entirely feasible.
Here's a listing of a Flask service, setting the CORS headers, grabbing data from a database, responding with JSON, and working happily with DickFeynman's approach on the client-side:
#!/usr/bin/env python
from __future__ import unicode_literals
from flask import Flask, Response, jsonify, redirect, request, url_for
from your_model import *
import os
try:
import simplejson as json;
except ImportError:
import json
try:
from flask.ext.cors import *
except:
from flask_cors import *
app = Flask(__name__)
#app.before_request
def before_request():
try:
# Provided by an object in your_model
app.session = SessionManager.connect()
except:
print "Database connection failed."
#app.teardown_request
def shutdown_session(exception=None):
app.session.close()
# A route with a CORS header, to enable your javascript client to access
# JSON created from a database query.
#app.route('/whatever-data/', methods=['GET', 'OPTIONS'])
#cross_origin(headers=['Content-Type'])
def json_data():
whatever_list = []
results_json = None
try:
# Use SQL Alchemy to select all Whatevers, WHERE size > 0.
whatevers = app.session.query(Whatever).filter(Whatever.size > 0).all()
if whatevers and len(whatevers) > 0:
for whatever in whatevers:
# Each whatever is able to return a serialized version of itself.
# Refer to your_model.
whatever_list.append(whatever.serialize())
# Convert a list to JSON.
results_json = json.dumps(whatever_list)
except SQLAlchemyError as e:
print 'Error {0}'.format(e)
exit(0)
if len(whatevers) < 1 or not results_json:
exit(0)
else:
# Because we used json.dumps(), rather than jsonify(),
# we need to create a Flask Response object, here.
return Response(response=str(results_json), mimetype='application/json')
if __name__ == '__main__':
##NOTE Not suitable for production. As configured,
# your Flask service is in debug mode and publicly accessible.
app.run(debug=True, host='0.0.0.0', port=5001) # http://localhost:5001/
your_model contains the serialization method for your whatever, as well as the database connection manager (which could stand a little refactoring, but suffices to centralize the creation of database sessions, in bigger systems or Model/View/Control architectures). This happens to use postgreSQL, but could just as easily use any server side data store:
#!/usr/bin/env python
# Filename: your_model.py
import time
import psycopg2
import psycopg2.pool
import psycopg2.extras
from psycopg2.extensions import adapt, register_adapter, AsIs
from sqlalchemy import update
from sqlalchemy.orm import *
from sqlalchemy.exc import *
from sqlalchemy.dialects import postgresql
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
class SessionManager(object):
#staticmethod
def connect():
engine = create_engine('postgresql://id:passwd#localhost/mydatabase',
echo = True)
Session = sessionmaker(bind = engine,
autoflush = True,
expire_on_commit = False,
autocommit = False)
session = Session()
return session
#staticmethod
def declareBase():
engine = create_engine('postgresql://id:passwd#localhost/mydatabase', echo=True)
whatever_metadata = MetaData(engine, schema ='public')
Base = declarative_base(metadata=whatever_metadata)
return Base
Base = SessionManager.declareBase()
class Whatever(Base):
"""Create, supply information about, and manage the state of one or more whatever.
"""
__tablename__ = 'whatever'
id = Column(Integer, primary_key=True)
whatever_digest = Column(VARCHAR, unique=True)
best_name = Column(VARCHAR, nullable = True)
whatever_timestamp = Column(BigInteger, default = time.time())
whatever_raw = Column(Numeric(precision = 1000, scale = 0), default = 0.0)
whatever_label = Column(postgresql.VARCHAR, nullable = True)
size = Column(BigInteger, default = 0)
def __init__(self,
whatever_digest = '',
best_name = '',
whatever_timestamp = 0,
whatever_raw = 0,
whatever_label = '',
size = 0):
self.whatever_digest = whatever_digest
self.best_name = best_name
self.whatever_timestamp = whatever_timestamp
self.whatever_raw = whatever_raw
self.whatever_label = whatever_label
# Serialize one way or another, just handle appropriately in the client.
def serialize(self):
return {
'best_name' :self.best_name,
'whatever_label':self.whatever_label,
'size' :self.size,
}
In retrospect, I might have serialized the whatever objects as lists, rather than a Python dict, which might have simplified their processing in the Flask service, and I might have separated concerns better in the Flask implementation (The database call probably shouldn't be built-in the the route handler), but you can improve on this, once you have a working solution in your own development environment.
Also, I'm not suggesting people avoid JQuery. But, if JQuery's not in the picture, for one reason or another, this approach seems like a reasonable alternative.
It works, in any case.
Here's my implementation of DickFeynman's approach, in the the client:
<script type="text/javascript">
var addr = "dev.yourserver.yourorg.tld"
var port = "5001"
function Get(whateverUrl){
var Httpreq = new XMLHttpRequest(); // a new request
Httpreq.open("GET",whateverUrl,false);
Httpreq.send(null);
return Httpreq.responseText;
}
var whatever_list_obj = JSON.parse(Get("http://" + addr + ":" + port + "/whatever-data/"));
whatever_qty = whatever_list_obj.length;
for (var i = 0; i < whatever_qty; i++) {
console.log(whatever_list_obj[i].best_name);
}
</script>
I'm not going to list my console output, but I'm looking at a long list of whatever.best_name strings.
More to the point: The whatever_list_obj is available for use in my javascript namespace, for whatever I care to do with it, ...which might include generating graphics with D3.js, mapping with OpenLayers or CesiumJS, or calculating some intermediate values which have no particular need to live in my DOM.
You make a bog standard HTTP GET Request. You get a bog standard HTTP Response with an application/json content type and a JSON document as the body. You then parse this.
Since you have tagged this 'JavaScript' (I assume you mean "from a web page in a browser"), and I assume this is a third party service, you're stuck. You can't fetch data from remote URI in JavaScript unless explicit workarounds (such as JSONP) are put in place.
Oh wait, reading the documentation you linked to - JSONP is available, but you must say 'js' not 'json' and specify a callback: format=js&callback=foo
Then you can just define the callback function:
function foo(myData) {
// do stuff with myData
}
And then load the data:
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = theUrlForTheApi;
document.body.appendChild(script);

Categories