Output data on same page after form submit - python

So I created a small flask program which would take a file , do some processing and returns a stream of data using yield.
I am using html form for file upload and submit. The form sends file to a python script and returns the output. The issue is that the output is presented onto a different page because of the form action attribute whereas I need the output on the same page. Probably inside a div tag.
index.html
<script>
if (!!window.EventSource) {
var source = new EventSource('/upload');
source.onmessage = function(e) {
console.log(e)
var byte = e.data;
var res = byte.split(/\s/);
console.log(res[0])
$("#morse").text(res[0].slice(1,));
}
}
</script>
<form action="/upload" method=post enctype=multipart/form-data >
<p><input type="file" name="file" >
<input type="submit" value="Upload" id="search_form_input">
</form>
<div id="morse" class="info">nothing received yet</div> // this is where is need my data
Python code
#app.route('/')
def index():
return render_template('index.html')
#app.route("/upload", methods=['GET', 'POST'])
def streambyte():
if request.method == 'POST':
f = request.files['file']
list_of_items = unAssign(f) # some file processing
def events():
for i in list_of_items:
yield "data: %s\n\n" % (i)
time.sleep(1) # an artificial delay
return Response(events(), content_type='text/event-stream')
This streams the data on http://localhost:5000/upload whereas I need it on http://localhost:5000.
I tried using redirect with Response but it failed saying TypeError: 'generator' object is not callable

You may not need JavaScript to do this...
Since you need the result on the 'index.html' page (i.e http://localhost:5000), you need to create two routes for the same index page.
The first route will load the fresh form (method attribute not set), while the second will reload the process form (method attribute is set to POST). Both routes will point to same index page.
Here below is how your code should look like:-
index.html
<!DOCTYPE html>
<html>
<head>
<title>Flask App - Output data on same page after form submit</title>
</head>
<body>
<form method=post enctype=multipart/form-data >
<p><input type="file" name="file" >
<input type="submit" value="Upload" id="search_form_input">
</form>
<div id="morse" class="info">nothing received yet</div>
<h3>{{ result }}</h3>
<h3>{{ file_path }}</h3>
<!-- this is where is need my data -->
</body>
</html>
Python code
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route("/", methods=['GET', 'POST'])
def streambyte():
# your file processing code is here...
f = request.files['file']
your_script_result = 'This variable holds the final data output'
# your file processing code is here...
return render_template('index.html', file_path = f, result = your_script_result)
if __name__ == '__main__':
app.run(debug=True)
Read more from this link: Send data from a textbox into Flask?

Related

Create API with Flask that receives 2 strings and returns the similarity between them with spacy [duplicate]

This question already has answers here:
Passing HTML to template using Flask/Jinja2
(7 answers)
Closed 4 months ago.
I have this code that compute the similarity between 2 strings:
import spacy
from spacy.lang.pt.examples import sentences
X ="some string 1"
Y ="some string 2"
nlp = spacy.load('pt_core_news_sm')
X_nlp = nlp(X)
Y_nlp = nlp(Y)
token_x = [token.text for token in X_nlp]
token_y = [token.text for token in Y_nlp]
print("Similarity:", X_nlp.similarity(Y_nlp))
Now I want to transform this code in an API with flask, I tried to follow a tutorial:
from flask import Flask,render_template,url_for,request
import re
import spacy
from spacy.lang.pt.examples import sentences
nlp = spacy.load('pt_core_news_sm')
app = Flask(__name__)
#app.route('/',methods=["POST"])
def process():
X_nlp = nlp(input())
Y_nlp = nlp(input())
print("Similarity:", X_nlp.similarity(Y_nlp))
if __name__ == '__main__':
app.run(debug=True)
the code above returns: "GET / HTTP/1.1" 405 -
You are trying to reach the URL "/". However, within your code there is no route defined for this path. Thus, the error 404 is returned.
You need a route that accepts both a GET and a POST request to both display a form and receive data from a submitted form sent via POST.
Otherwise, a 405 error is returned because the request method is not allowed.
#app.route('/', method=['GET', 'POST'])
def index():
if request.method == 'POST':
# Handle a POST request and the data sent here.
# ...
Within Flask it is not possible to request input with input().
As mentioned above, you need a form within an HTML page. Within this form you can define input fields that must be provided with a name attribute in order to query them on the server side.
<form method="POST">
<input type="text" name="x" />
<input type="text" name="y" />
<input type="submit">
</form>
If the submit button is now pressed, the data of the form, as defined in the method attribute, is sent to the server via POST and can be queried here. The input fields are queried using the name attribute.
Finally, the endpoint must have a return value. In your case, this is the template that displays the page with the form and outputs the possible result.
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
x = request.form.get('x', '')
y = reutest.form.get('y', '')
# ...
return render_template('index.html')
So the entire code of your application should look something like this.
Flask (app.py)
from flask import (
Flask,
render_template,
request
)
import spacy
nlp = spacy.load('pt_core_news_sm')
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
# Handle a POST request and the data sent here.
x_nlp = nlp(request.form.get('x', ''))
y_nlp = nlp(request.form.get('y', ''))
resultado = x_nlp.similarity(y_nlp)
# Return a rendered template and pass defined variables to the template.
return render_template('index.html', **locals())
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Index</title>
</head>
<body>
<form method="POST">
<input type="text" name="x" />
<input type="text" name="y" />
<input type="submit">
</form>
{% if resultado -%}
<p>Similaridade: {{ resultado }}</p>
{% endif -%}
</body>
</html>

Python Flask: getting URL when moving to production

I'm using Python from quite some time but I am totally newbie in Flask. Can you help me with two simple questions I have been strugling two days?
I have a simple Flask app that supposed to import a XLSX or CSV, parse it and create a zip file to download. While I am begginig work on the parse part, I got an error when uploading the file, and I found out that the app is not saving the file altough it works when running flask locally.
This is the code:
test2.py
``` from flask import Flask, render_template, request
from werkzeug.utils import secure_filename
import pandas as pd
app = Flask(__name__)
def work(arquivo):
df = pd.read_excel(arquivo)
return(str(df.shape))
#app.route('/')
def start():
return "acesse /upload"
#app.route('/upload')
def upload_file():
return render_template('upload.html')
#app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save('./inbox/'+secure_filename(f.filename))
a = work('./inbox/'+secure_filename(f.filename))
return 'file uploaded successfully '+a
if __name__ == '__main__':
app.run(debug = True)
```
And this is the upload.html file that I put on templates folder in production (the app runs on http://www.fabianocastello.com.br/test2/upload):
<html>
<body>
<form action = "http://www.fabianocastello.com.br/test2/uploaded" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
when locally, the upload.html file that works is this:
<html>
<body>
<form action = "http://localhost:5000/uploader" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
The error I got after uploading the file is this:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
My questions are these:
1. Why the production app is not saving the uploaded file in "inbox" folder?
2. Is there a way that I substitute the URL in upload.html file from a variable so to I do not have to manually change the file before upload?
Thank you all in advance.
This is what the url_for method is for. That will automatically fix "http://localhost:5000/uploader" for you.
However, <form action = "http://www.fabianocastello.com.br/test2/uploaded" ...> points at a bigger misunderstanding. It would be horrendous if you had to alter every route in your templates moving from development to production. Your Flask routes needn't point to the specific domain that you're running your app on; they need only point to the endpoint of the server running your app (which might be gunicorn, for example). The Mega Tuorial might be helpful here for deployment. There's also more info in the deployment docs.
With that out of the way, there's other issues that need to be dealt with:
You have two route functions with the same name - upload_file. Why? It doesn't matter that you decorated them with different URLs, you can't have two functions with the same name in the same namespace.
The second upload_file is set to accept both GET and POST requests, but you only handle the POST case. Sending a GET request to this route will error.
This fixes the form:
<html>
<body>
<form action = "{{ url_for('upload_file') }}" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
This consolidates the two route functions into one:
#app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save('./inbox/'+secure_filename(f.filename))
a = work('./inbox/'+secure_filename(f.filename))
return 'file uploaded successfully '+a
else:
return render_template('upload.html')
return 'file uploaded successfully '+a is going to give a garbage result, if any, though. It's not going to render a template with the message, it's just going to be unstyled text. It looks like you want AJAX, which would look something like this:
<html>
<body>
<form action = "{{ url_for('upload_file') }}" method = "POST"
enctype = "multipart/form-data" id="upload_file_form">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
<div id="response_div"></div>
</body>
<script>
$("#upload_file_form").submit(function(e) {
e.preventDefault();
var form = $(this);
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: form.serialize(),
context: form,
success: function(resp) {
$("#response_div").html(resp);
}
});
});
</script>
</html>

String POST Request to Flask

I'm trying to implement a simple dashboard with Flask that will:
Accept a user text input, with a "submit" button. POST this user input to flask.
Flask accepts this input, does some stuff to it, then makes a GET request to another API.
This GET request returns data and shows it somehow (can just be console.log for now)
As an example, with the star wars API:
User inputs name of a Star Wars character (assume no spelling errors)
Flask reads this input name, and maps it to an ID number, because the Star Wars API accepts id numbers. Form a GET request to the Star Wars API, to get full character information.
For now, we can just console.log character information (e.g. "height", "mass", etc.)
What I have now:
app.py
from flask import Flask, jsonify, request, render_template
import random
import json
app = Flask(__name__)
#app.route("/")
def index():
return render_template('index.html')
#app.route("/form_example", methods=["GET", "POST"])
def form_example():
if request.method == "POST":
language = request.form("character_name")
starwars_dictionary = {"Luke Skywalker":"1", "C-3PO":"2", "R2-D2": "3"}
# starwars_dictionary is a dictionary with character_name:character_number key-value pairs.
# GET URL is of the form https://swapi.co/api/people/<character_number>
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title>py-to-JS</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<h3>Sample Inputs</h3>
<ul>
<li>Luke Skywalker</li>
<li>C-3PO</li>
<li>R2-D2</li>
</ul>
<form method="POST">
Enter Name: <input type="text" name="character_name"><br>
<input type="submit" value="Submit"><br>
</form>
</body>
</html>
In this current form, when I run the app, it returns "Method not allowed; this method is not allowed for the requested URL".
I'm not sure what I'm missing; it's probably just not wired together properly but I'm not sure what the proper syntax is.
Working version after implementing the accepted answer:
app.py
from flask import Flask, jsonify, request, render_template
import requests
import random
import json
app = Flask(__name__)
#app.route("/index", methods=["GET", "POST"])
def index():
#character_height = "" # init a default value of empty string...seems unwieldy
if request.method == "POST":
character_name = request.form.get("character_name")
# Map user input to a numbers
starwars_dictionary = {"Luke Skywalker":"1", "C-3PO":"2", "R2-D2": "3"}
char_id = starwars_dictionary[character_name]
url = "https://swapi.co/api/people/"+char_id
response = requests.get(url)
response_dict = json.loads(response.text)
character_height = response_dict["height"]
return render_template("index.html", character_height=character_height)
return render_template("index.html")
##app.route("/form_example", methods=["GET", "POST"])
#def form_example():
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title>py-to-JS</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<h3>Sample Inputs</h3>
<ul>
<li>Luke Skywalker</li>
<li>C-3PO</li>
<li>R2-D2</li>
</ul>
<form method="POST" action="/index">
Enter Name: <input type="text" name="character_name"><br>
<input type="submit" value="Submit"><br>
</form>
{{ character_height }}
</body>
</html>
Probably the form is posting to the / endpoint, because you didn't declare a form action.
Needs to be more like:
<form method="POST" action="/form_example">
Or if you want to get snazzy and use Jinja's url_for function:
<form method="POST" action="{{ url_for('form_example') }}">
EDIT: That said, you could handle this with a single route function:
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
language = request.form("character_name")
starwars_dictionary = {"Luke Skywalker":"1", "C-3PO":"2", "R2-D2": "3"}
# Logic to query remote API ges here.
else: # Assume method is GET
return render_template("index.html")
Then make the form action {{ url_for('index') }}

Flask does not render my modified page when asked [duplicate]

This question already has answers here:
Reload Flask app when template file changes
(13 answers)
Closed 4 years ago.
I have a page with a simple text and a button in a form
<html>
<head>
</head>
<body>
<h1>JASON</h1>
<form>
<button type="submit" formmethod="POST">Activate</button>
<br>
<input type="hidden" value="act.12344" name="sub" />
</form>
</body>
</html>
And this python script
from flask import Flask, render_template, request, redirect
app = Flask(__name__)
#app.route('/atdt', methods=['GET', 'POST'])
def atdt():
if request.method == 'POST':
print('post')
requested = request.form['sub']
ver = str(requested).split('.')
if ver[0] == 'act':
print('act')
modif(ver[1]) #this func modifies the index page
return render_template('index.html')
else:
return render_template('index.html')
The point of the script is to change the name jason in something else...and it works well, the page is changed and its all good
But my flask program wont show it...the '.html' page its changed, and if I open it manually, outside the program it works!
But if i give python the line return render_template('index.html') but it wont render it
If i try to refresh manually it will just show me the old page
Any help ?
You are not modifying the html, You are just calling a function that returns modified version of an input!
First of all you have to use tempalte engine
Your HTML Should be something like this:
<html>
<head>
</head>
<body>
<h1>{{name}}</h1>
<form>
<button type="submit" formmethod="POST">Activate</button>
<br>
<input type="hidden" value="act.12344" name="sub" />
</form>
</body>
</html>
And your view should look like this:
#app.route('/atdt', methods=['GET', 'POST'])
def atdt():
if request.method == 'POST':
print('post')
requested = request.form['sub']
ver = str(requested).split('.')
if ver[0] == 'act':
print('act')
name = modif(ver[1]) #this func modifies the index page
return render_template('index.html', name=name)
else:
return render_template('index.html', name="JASON")
The template engine will handle The name change
Flask uses Jinja2 Template Engine, You can read more about it here

Flask: How to handle application/octet-stream

I want to make a multiple file-upload form.I use jQuery File Uploader. My server-side code:
#app.route("/new/photogallery",methods=["POST"])
def newPhotoGallery():
print request.files
I tried two things:
Submit form normally:
When i submit my form normally,it prints:
ImmutableMultiDict([('post_photo_gallery', FileStorage: u'' ('application/octet-stream'))])
Submit form using AJAX:
When i submit my form using AJAX,it prints:
ImmutableMultiDict([])
My first question is: Why is there a difference between AJAX request and normal request.
My second question is: How can i handle this application/octet-streamrequest in Flask/Python
My third question is: Is this a good way to use application/octet-stream ?
By the way i do not know much about application/octet-stream.Thank you very much.
Regardless of the the data encoding, you should be able to get the raw data with request.data.
In the case of application/octet-stream, you can just write request.data to a binary file.
An example handler for various data types:
from flask import json
#app.route('/messages', methods = ['POST'])
def api_message():
if request.headers['Content-Type'] == 'text/plain':
return "Text Message: " + request.data
elif request.headers['Content-Type'] == 'application/json':
return "JSON Message: " + json.dumps(request.json)
elif request.headers['Content-Type'] == 'application/octet-stream':
with open('/tmp/binary', 'wb') as f:
f.write(request.data)
f.close()
return "Binary message written!"
else:
return "415 Unsupported Media Type ;)"
The typical scenario of handling form data is already documented here.
I was unable to get a request working using application/octet-stream type posts, but have used multipart/form-data type forms in the past to upload images using flask.
I have extended what I have done in the past to support multiple upload files and this has worked leveraging werkzeug's FileStorage objects.
The key here is setting up a post based route that is looking for a request element from a form. This should allow you to POST to the route either via a standard form or an AJAX call.
Below is a simplified example that is using a form:
Template for the view:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>jQuery File Upload Example</title>
</head>
<body>
{% if err %}
<h4>{{ err }}</h4>
{% endif %}
<form action="/" method=POST enctype=multipart/form-data id="fileupload">
<input type="file" name="files" data-url="/" multiple>
<input type=submit value=Post>
</form>
{% if files %}
{% for file in files %}
<p>Uploaded: <b>{{ file }}</b> </p>
{% endfor %}
{% endif %}
</body>
</html>
Flask App
from flask import Flask, request, render_template
from werkzeug import secure_filename, FileStorage
import os
# Flask functions
app = Flask(__name__)
app.config.from_object(__name__)
DEBUG = True
# add this so that flask doesn't swallow error messages
app.config['PROPAGATE_EXCEPTIONS'] = True
#app.route('/', methods=['GET', 'POST'])
def uploader():
if request.method =='POST' and request.files.getlist('files'):
up_file_list = []
# Iterate the through a list of files from the form input field
for a_file in request.files.getlist('files'):
if a_file.filename:
# Validate that what we have been supplied with is infact a file
if not isinstance(a_file, FileStorage):
raise TypeError("storage must be a werkzeug.FileStorage")
# Sanitise the filename
a_file_name = secure_filename(a_file.filename)
# Build target
a_file_target = os.path.join('/tmp/', a_file_name)
# Save file
a_file.save(a_file_target)
up_file_list.append(a_file_name)
# Return template
if up_file_list:
return render_template('uploader.html', err=None, files=up_file_list)
else:
return render_template('uploader.html', err='No Files Uploaded', files=None)
else:
return render_template('uploader.html', err=None, files=None)
# application execution
if __name__ == '__main__':
app.run()

Categories

Resources