How to update image in real time with flask? - python

I am using flask (python) to host a interface that allows the user to upload an image, and use a slider to change the value of a variable in a function that reduces noise in an image.
The slider works but the problem is I have to reload the page every time that I want to see the change in value on the updated image.
how can I add a slider that will update the image in real time so I won't have to continuously reload the page to see the changes?
(If the slider is at 0 I want it to to look how the image did when it first uploaded)
I'm doing some searching and it looks like I would use jquery or something but I don't know how to implement it
thanks for reading
app.py:
import os
from flask import Flask, render_template, request, send_from_directory,url_for, session, redirect
import cv2
import shutil
app = Flask(__name__)
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
app.config['UPLOAD_FOLDER'] = os.path.join(APP_ROOT, 'images')
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
#app.route("/")
def index():
session.clear()
return render_template("upload.html")
#app.route('/images/<filename>')
def uploadfile(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],filename)
#app.route('/home')
def home():
return render_template("completed.html", imgSrc="images/" + session.get('imgSrc') , message=session.get('message'))
#app.route("/upload" , methods = ['POST'])
def upload():
target = os.path.join(APP_ROOT, 'images')
if request.method == 'POST':
if not os.path.isdir(target):
os.mkdir(target)
for file in request.files.getlist("file"):
filename = file.filename
destination = "/".join((target, filename))
file.save(destination)
filename = destination
org = open(filename, 'rb')
base = os.path.basename(filename)
dir = os.path.dirname(filename)
filename_cp = os.path.splitext(base)[0]
filename_cp = "cp_"+filename_cp+os.path.splitext(base)[1]
destination2 = dir+"/"+filename_cp
file.save(destination2)
cpy = open (destination2, 'wb')
shutil.copyfileobj(org, cpy)
session['image'] = filename
session['filename'] = filename
session['imgSrc'] = os.path.basename(destination)
session['cimgsrc'] = os.path.basename(destination2)
session['cpimg'] = destination2
print("session", session)
return render_template("completed.html",imgSrc="images/"+session.get('imgSrc'))
#app.route("/imgp/nr", methods=['post'])
def nr():
print(session)
img = cv2.imread(session.get('cpimg'), 0)
#threshold = 40
threshold = float(request.form['slider'])
cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY, img)
print (session['cpimg'])
cv2.imwrite(session.get('cpimg'),img)
session['message'] = "NR is done!"
session['imgSrc'] = os.path.basename(session['cpimg'])
return redirect(url_for('home', op='nr'))
if __name__ =="__main__":
app.secret_key = "abcdefghijklmnopqrstuvwxyz"
app.run(port = 4555, debug = True)
upload.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload</title>
</head>
<form id ="upload-form" action="{{ url_for ('upload') }}" method = "POST" enctype="multipart/form-data">
<input type="file" name="file" accept = image/* multiple>
<p>Drag your files here or click in this area.</p>
<button type ="submit" value="Upload"> Upload</button>
</form>
<body>
</body>
</html>
completed.hmtl (where the slider and uploaded image is):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1> file uploaded</h1>
<img src="{{imgSrc}}" style="height:200px;width:300px;"/>
<h2>{{message}}</h2>
<form action ="imgp/nr" method = "post">
<input type="range" min={{min}} max={{max}} value={{max}} class="slider" id="myRange" name="slider">
<input type="submit" value="Apply ga">
</form>
</body>
</html>

The short answer is you will need to do this with Javascript/jQuery. There's a similar question here covers two ways to do it:
Either load the (base64 converted) image via ajax; or
Change the src of the image on the page using jquery, to have it reload.
You will likely need to change a few parts of your code to get this all working, but it may be something along the lines of:
app.py:
Change your nr() function to GET, and return a base64 encoded image, see here for example (upload_file() function)
completed.html:
Add an ajax (the following is jquery) call to /imgp/nr that changes the display of the image on the page, when the slider is changed. For example:
...
<img id="yourImage" style="height:200px;width:300px;">
...
<script type="text/javascript">
$("#myRange").change(function(){
var sliderVal = $(this).val();
$.ajax({
medthod: 'POST',
url:'/imgp/nr/',
data: JSON.stringify({slider: sliderVal}),
success: function(data){
$("#yourImage").attr('src', 'data:image/png;base64, ' + data);
}
});
});
</script>
This code may need some fixing, I don't have much time to make it perfect sorry. Hopefully you get the idea!

Related

flask send_file serves html?

I have an MWE of the problem in the following html template:
<block body>
<h2> Test Report </h2>
<form method="post">
<input type="submit" value="Generate Report">
</form>
</block body>
With the associated flask blueprint:
#bp.route('/reports2', methods=('GET', 'POST'))
#login_required
def reports2():
if request.method == 'POST':
from io import BytesIO
byte_obj = BytesIO()
byte_obj.write(b'Hello, I am a test file')
return send_file(byte_obj, as_attachment=True, download_name='test-downloaded-report.txt', mimetype='text/plain')
return render_template('report/reports2.html')
The result is bewildering. There's no file created in my downloads directory called 'test-downloaded-report.txt'. Instead, it downloads reports2.html!
I also originally just had send_file without being returned, instead returning a redirect to reports2.html so that the form would reset. Nothing happened there, so I assume the return is necessary for anything to happen, but as an aside I also don't see how I should send a file for download and redirect to a different page.
Any idea why that file is getting sent, instead of the file I created?
The file is not served as expected because the pointer is at the end of the stream. By putting the pointer at the beginning, the data can be read completely and should download as expected.
#bp.route('/reports2', methods=('GET', 'POST'))
#login_required
def reports2():
if request.method == 'POST':
from io import BytesIO
byte_obj = BytesIO()
byte_obj.write(b'Hello, I am a test file')
byte_obj.seek(0)
return send_file(
byte_obj,
as_attachment=True,
download_name='test-downloaded-report.txt',
mimetype='text/plain'
)
return render_template('report/reports2.html')
Forwarding after downloading or resetting the form is a little more complicated. As you correctly noticed, send_file is a response from the server. It cannot be used in conjunction with a redirect.
However, the following suggestion shows you how to use JavaScript to send the form, download the file and then reset the form. With a little adjustment you should also be able to forward to another location.
from flask import (
Flask,
render_template,
request,
send_file
)
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
from io import BytesIO
byte_obj = BytesIO()
byte_obj.write(b'Hello, I am a test file')
byte_obj.seek(0)
return send_file(
byte_obj,
as_attachment=True,
download_name='test-downloaded-report.txt',
mimetype='text/plain'
)
return render_template('index.html')
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Generate Report</title>
</head>
<body>
<h2>Test Report</h2>
<form name="my-form" action="{{ url_for('index') }}" method="post">
<input type="submit" value="Generate Report">
</form>
<script type="text/javascript">
(function() {
const download = (url, form, callback) => {
fetch(url, {
method: 'post',
body: new FormData(form)
}).then(resp => {
resp.ok && resp.blob().then(blob => {
callback(blob, resp.headers);
});
});
};
const save = (data, filename) => {
const elem = document.createElement('a');
elem.href = URL.createObjectURL(data);
elem.download = filename;
elem.click();
};
const form = document.querySelector('form[name="my-form"]');
form.addEventListener('submit', evt => {
evt.preventDefault();
download(evt.target.action, evt.target, (data, headers) => {
// Extract the filename from the "Content-Disposition" header.
const disposition = headers.get('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
const filename = matches[1].replace(/['"]/g, '');
save(data, filename);
}
}
});
// Reset the form or redirect to another location.
evt.target.reset();
})
})();
</script>
</body>
</html>

Any POST request creates error 413

I'm trying to host a website off a Beaglebone Black using lighttpd and web.py. Any post request creates Error 413 - Request Entity Too Large. I am very new to web development, so I apologize if I've used the wrong terminology. To be clear, I am not attempting to upload any file of any kind. I created a simple page, test, to illustrate the error. My code:
First, Monitor.py:
#!/usr/bin/env python
import web
import Model
from Account import Account
from Forgot import Forgot
from Login import Login
from Reset import Reset
from Subscribe import Subscribe
from Success import Success
from Status import Status
from Test import Test
urls = ('/', 'Status',
'/test', 'Test',
'/account', 'Account',
'/forgot', 'Forgot',
'/login', 'Login',
'/reset/(\d+)', 'Reset',
'/success', 'Success',
'/subscribe', 'Subscribe',
)
render = web.template.render('templates')
app = web.application(urls, globals())
web.config.debug = False
session = Model.create_session(app)
if __name__ == '__main__':
app.run()
Now, Test.py:
#!/usr/bin/env python
import web
import Model
render = web.template.render('templates')
class Test:
def GET(self):
return render.test()
def POST(self):
i = web.input()
raise web.seeother('/test')
Now, the html file, located in the templates folder:
$def with()
<html>
<head>
<link rel="stylesheet" href="static/stylesheet.css" type="text/css" media="screen" charset="utf-8"/>
<title>Account Page</title>
</head>
<body>
div>
<form action="/test" method="POST">
<table class="std-element">
<tr>
<th>
<input type="text" name="example_text">
</th>
</tr>
</table>
</form>
</div>
</body>
</html>
The relevant portion of the model file:
session = None
def create_session(app):
global session
session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'username':default_username})
session['username'] = default_username
return session
def get_session():
global session
return session
And, finally, the lighttpd.config file:
server.modules = (
"mod_access",
"mod_accesslog",
"mod_alias",
"mod_compress",
)
server.document-root = "/var/www/"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/var/run/lighttpd.pid"
server.username = "www-data"
server.groupname = "www-data"
server.port = 80
server.breakagelog = "/var/log/lighttpd/breakage.log"
server.max-request-size=100000000
server.uploads-dirs=("/mnt")
server.network-backend="write"
## Use ipv6 if available
#include_shell "/usr/share/lighttpd/use-ipv6.pl"
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/x-javascript", "text/css", "text/html", "text/plain" )
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
server.max-request-size=100000000 is way too large. It is measured in KB. Internally, lighttpd left-shifts that number 10 bits, which, in your version of lighttpd, results in any POST being compared with 0 and rejected because it is too large. This has been fixed in lighttpd 1.4.40, but you should probably reduce that limit anyway. Having a 100 TB (!) request size limit is probably not going to protect your Beaglebone. Try this: server.max-request-size=1024 will set a 1 MB limit on POST requests. (Again, that config directive is in KB)

Django - How to use asynchronous task queue with celery and redis

#In my views.py file
pi1 = None
pis1 = None
def my_func():
#Essentially this function sets a random integer to pi1 and pis1
global pi1, pis1
pi1 = randint(0,9)
pis1 = randint(0,9)
return
def index(request):
my_func()
context = {
"pi1" : pi1,
"pis1" : pis1,
}
return render(request, "index.html", context)
#In the index.html file
<h1>{{ pi1 }}</h1>
<h1>{{ pis1 }}</h1>
I've removed a lot of my code for simplicity, but this is the gist of it. Despite the code that I've posted for my_func, it is a time consuming function that causes index.html to load for awhile when it is accessed. How would I run my_func in the backround using celery and redis so that index.html loads more quickly?
I've read the celery documentation, but I am still having trouble setting up celery and redis. Thank you.
As is said previous, you might not need celery. Here's an example derived from case 2 of this: https://zapier.com/blog/async-celery-example-why-and-how/. It's fully working for me:
from time import sleep
import json
from django.http import HttpResponse
from django.shortcuts import render
def main_view(request):
return render(request, 'index.html')
def ajax_view(request):
sleep(10) #This is whatever work you need
pi1 = "This is pi1" #I just made pi1/pis1 random values
pis1 = "This is pis1"
context = {
"pi1" : pi1,
"pis1" : pis1,
}
data = json.dumps(context)
return HttpResponse(data, content_type='application/json')
My index.html contains:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Main View</title>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script>
$(document).ready(function(){
$.ajax({
url: "/test_ajax/",
}).done(function( data) {
$("#pi1").text(data.pi1);
$("#pis1").text(data.pis1);
});
});
</script>
</head>
<body>
<h1 id = "pi1">Loading</h1>
<h1 id = "pis1">Loading</h1>
</body>
</html>
And my urls.py contains:
from django.conf.urls import include, url
from django.contrib import admin
from testDjango.test import main_view, ajax_view
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^test/', main_view),
url(r'^test_ajax/', ajax_view)
]
What happens when I visit localhost:8000/test/ is that I instantly see:
After about 10 seconds, I then see:
The idea is that you return your page instantly and use jquery to fetch the result of the operation whenever that's finished and update your page accordingly. You can add more things like progress bars/loading image etc. For your example, you can do the processing for pi1 and pis in the background and load it into the HTML after that's finished.
You don't need celery here. You can load these values on page with AJAX request. You should create a separate view that will calculate this values and after the index.html is loaded call it with javascript.

web.py file upload using the built in File form

I'm trying to make a working file upload form using the built in input form. It works just fine with a 'static' html input form (using shutil.copyfileobject), but it won't work using the built in one.
import web, shutil
from web import form
urls = ('/', 'Index')
app = web.application(urls, globals())
render = web.template.render('templates/')
fileForm = form.Form(form.File('myfile'))
class Index(object):
def GET(self):
f = fileForm()
return render.index(f)
def POST(self):
f = fileForm()
fi = f['myfile']
mydir = 'uploads/'
shutil.copy(fi, mydir)
if __name__ == "__main__":
app.run()
And templates/index.html
$def with (f)
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
</head>
<body>
<form name="main" method="post" enctype="multipart/form-data" action="">
$:f.render()
<input type="submit">
</form>
</body>
</html>
Errors:
<type 'exceptions.TypeError'> at /
coercing to Unicode: need string or buffer, File found
Python
C:\Python27\lib\ntpath.py in abspath, line 486
Web
POST http://localhost:8080/
Traceback (innermost first)
C:\Python27\lib\ntpath.py in abspath
479.
480.else: # use native Windows method on Windows
481. def abspath(path):
482. """Return the absolute version of a path."""
483.
484. if path: # Empty path must return current working directory.
485. try:
486. path = _getfullpathname(path) ...
487. except WindowsError:
488. pass # Bad path - return unchanged.
489. elif isinstance(path, unicode):
490. path = os.getcwdu()
491. else:
492. path = os.getcwd()
The built in File doesn't seem to return an object, so shutil.copyfileobject does not seem to work.
Please help me sort it out.
import web, shutil
from web import form
urls = ('/', 'Index')
app = web.application(urls, globals())
render = web.template.render('templates/')
fileForm = form.Form(form.File('myfile'))
class Index(object):
def GET(self):
f = fileForm()
return render.index(f)
def POST(self):
x = web.input(myfile={})
filedir = '/tmp/'
if 'myfile' in x:
filepath=x.myfile.filename.replace('\\','/')
filename=filepath.split('/')[-1]
fout = open(filedir +'/'+ filename,'w')
fout.write(x.myfile.file.read())
fout.close()
if __name__ == "__main__":
app.run()

Returning selective Data from mongodb as specified in Flask

i have a webservice that takes data from mongodb and returns it over a local web service.
I want to implement a query based control over here that would allow filtering the webservice data from a GUI.
Currently my html file is using the code:
<!DOCTYPE html>
<html lang="en">
<body>
<h1>Welcome to App</h1>
<h2>Enter your username</h2>
<form action="." method="POST">
<input type="text" name="text">
<input type="submit" name="my-form" value="Submit">
</form>
</body>
</html>
and my app.py which is running on flask looks like:
from flask import Flask
from flask import render_template
from flask import request
from pymongo import Connection
import json
from bson import json_util
from bson.json_util import dumps
app = Flask(__name__)
MONGODB_HOST = '172.16.1.95'
MONGODB_PORT = 27017
DBS_NAME = 'AllTables'
COLLECTION_NAME = 'SubjectViews'
#app.route('/')
def my_form():
return render_template("index.html")
#app.route('/testtag', methods=['POST'])
def my_form():
text = request.form['text']
processed_text = " + text + "
return processed_text
def donorschoose_projects():
connection = Connection(MONGODB_HOST, MONGODB_PORT)
collection = connection[DBS_NAME][COLLECTION_NAME]
projects = collection.find({"tags":"processed_text"})
json_projects = []
for project in projects:
json_projects.append(project)
json_projects = json.dumps(json_projects, default=json_util.default)
connection.disconnect()
return json_projects
if __name__ == "__main__":
app.run(host='0.0.0.0',port=5001,debug=True)
What i am trying to do is to get a input from the user in index.html, pass it into the collection.find parameter in app.py so that on my webservice URL i will get data filtered for that particular user. Unfortunately i am running into errors.
Any help would be much appreciated.
Thank you,
Anmol
EDIT: No luck so far!

Categories

Resources