How to pass HTML form input to Python script? - python

I have form tags like this
<form name="input_form" action="/test.py" method="get">
Set Frequency: <input type="number" name="set_freq" id="set_freq"> <br>
<input type="submit" value="Submit" />
<input id="reset" type="reset" value="Reset" /><br>
</form>
I need to have it where, the user enter 1500 or whatever value, and then once submit is hit, that number is passed to the python program, test.py for right now.

There is more than one way to do that, you can use CGI (a webserver that calls your python script and serves the output) or a WSGI framework like Django or Flask, or others. Of course, these also need to be served, either from a "typical" webserver like apache or Nginx or something like Gunicorn.
my recommendation is to use a framework like Flask to accomplish that task.
install it by running, preferably in a virtual env:
pip install flask
then you could write something simple as
from flask import Flask, request, render_template
# Flask constructor
app = Flask(__name__)
# A decorator used to tell the application
# which URL is associated function
#app.route('/test.py', methods =["GET", "POST"])
def get_freq():
if request.method == "POST":
# getting input with freq = set_freq in HTML form
freq = request.form.get("set_freq") # <--- do whatever you want with that value
return "Your freq value is " + freq
return render_template("form.html")
if __name__=='__main__':
app.run()
then you will need to provide your form.html template to be served, and put it in the ./templates folder.
<form action="{{ url_for("get_freq") }}" method="post">
<label for="set_freq">Set Frequency:</label>
<input type="number" id="set_freq" name="set_freq" >
<button type="submit">submit</button>
now run the script and open http://127.0.0.1:5000/test.py
you will be served with the form and by submitting it you will send the freq value straight to the backend where you can interpret it.
for learning purposes:
for learning purposes, you can use the HTTP server in the python standard library without using a framework. this not intended for production and even could take more effort than using web frameworks (this is the point of using such frameworks).
#!/usr/bin/env python3
"""
Usage::
./server.py [<port>]
"""
from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
from urllib.parse import urlparse, parse_qs
class S(BaseHTTPRequestHandler):
def _set_response(self, code):
self.send_response(code)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_GET(self):
logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers))
query_components = parse_qs(urlparse(self.path).query)
if query_components.get('set_freq'):
freq = query_components.get('set_freq') # <--- do whatever you want with that value
print(freq)
self._set_response(200)
self.wfile.write("Python HTTP server received your GET request".encode("utf-8"))
def run(server_class=HTTPServer, handler_class=S, port=8080):
logging.basicConfig(level=logging.INFO)
server_address = ('', port)
httpd = server_class(server_address, handler_class)
logging.info('Starting httpd...\n')
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
logging.info('Stopping httpd...\n')
if __name__ == '__main__':
from sys import argv
if len(argv) == 2:
run(port=int(argv[1]))
else:
run()
run the web server and as it starts to listen to incoming requests you can now open your form page and submit your data. you'll need to edit the path where your form will send the data, here is the final snippet.
<!DOCTYPE html>
<html>
<body>
<h2>HTML Forms</h2>
<form name="input_form" action="http://localhost:8080">
Set Frequency: <input type="number" name="set_freq" id="set_freq"> <br>
<input type="submit" value="Submit" />
<input id="reset" type="reset" value="Reset" /><br>
</form>
<p>If you click the "Submit" button, the form data will be sent to your local Python 3 HTTP server.</p>
</body>
</html>
again this is nowhere a complete or optimized or production code.
use it just in case you want to get something up and running to play with and learn.
code Explanation:
BaseHTTPRequestHandler class is used to handle the HTTP requests that arrive at the server. By itself, it cannot respond to any actual HTTP requests; it must be subclassed to handle each request method (e.g. GET or POST). so we extend it in S class by adding the do_GET method that will be called on GET requests.
from Python official doc:
The handler will parse the request and the headers, then call a method
specific to the request type. The method name is constructed from the
request. For example, for the request method SPAM, the do_SPAM()
method will be called with no arguments. All of the relevant
information is stored in instance variables of the handler. Subclasses
should not need to override or extend the init() method.
read more from here:
https://docs.python.org/3/library/http.server.html#http.server.HTTPServer

Related

Python Flask App Deployed to IIS Webserver 500's when using Subprocess nslookup

I have a simple flask app that works locally but gets 500'd when testing in IIS.
Edit: I was wrong, initially thought was pandas read issue but the issue is actually coming from subprocess that tries to get the user's IP address:
from flask import Flask, request
import subprocess
app = Flask(__name__)
html = '''
<h1>Test</h1>
<h2>Report Generator</h2>
<p>
<form action="/submitted" method="post">
<label for="reports">Choose a Report:</label>
<select id="reports" name="reports">
<option value="user_test">User Test</option>
</select>
<input type="submit">
</form>
'''
#app.route("/")
def index():
return html
#app.route("/submitted", methods=['POST'])
def show():
select = request.form.get("reports")
if select == 'user_test':
name = 'XXXXXXXX.dcm.com'
result = subprocess.check_output(['nslookup', name])
else:
result = "Not Available"
return result
if __name__ == "__main__":
app.run()
This code runs fine when tested locally. If I remove the part where it runs subprocess to get user IP that works fine on IIS. The trouble is when I try to include the part that runs subprocess.check_output(['nslookup',name]) when running on IIS, which leads to 500 internal server error.
Here is picture of error:
Thanks for the help!
1.You need "import pandas as pd" at top.
2.The error doesn't happen when reading csv, but on the return process.
"df.values" cannot be returned at this situation because "df" is Dataframe type. Instead, you can use:
df = pd.read_csv("uuids.csv")
return df.to_html(header="true", table_id="table")
or
return df.to_csv()
or
return render_template("xxx.html"......)

Why is functions-framework returning a 400 status code when i send it a file?

I am developing a micro services architecture locally on my laptop, where a flask app communicates with a Google cloud function (using functions-framework library).
In it,the following happens:
The user uploads a file to a form. Upon submit, the file is sent to Flask
Flask will then extract the file from the request and then send the file to the cloud function, running on port 8080
When the file is sent though the cloud function is returning a 400 status code (bad request).
What am I doing wrong?
===form.html===
<html>
<body>
<h1>Report</h1>
<form action = "http://localhost:3000**/process**" method = "post" enctype = "multipart/form-data">
<input type = "file" name = "file" />
<input type = "hidden" name = "report" />
<p><input type = "submit" value = "submit" /></p>
</form>
</body>
</html>
===Flask===
...
#app.route('/process',methods = ['POST'])
def process():
f = request.files['file']
files = {'document': f.read()}
headers = {'Content-type': 'multipart/form-data'}
url='http://127.0.0.1:8080'
r = requests.post(url,files=files,headers=headers)
if r:
return redirect(url_for('success',tool=r.text))
#app.route('/form')
def form():
return render_template('form.html')
if __name__ == '__main__':
app.run(port=3000)
===functions-framework===
def func2(request):
file=request.files['document']
return str(type(file))
In the documentation for Functions Framework, there are more links to learn more about this, particularly on how to use the Functions Framework for Python runtime. Here you can find a quickstart about error handling.
In this answer from another question, you could check how to implement an error handler for a given code error and get the description of the error in order to debug your code and know why you're getting the 400 status code error.

Advice on Deploying a serious of scripts to web server (Java, Python) [duplicate]

This question already has answers here:
Are a WSGI server and HTTP server required to serve a Flask app?
(3 answers)
Closed 4 years ago.
Currently, I am writing a workflow for data processing and trying to deploy it on a web server, in the whole workflow. I had to use two different languages (Java and Python).
I have an HTML form that takes input from the user, then my python flask app will process that input and pass it to a java program using call function built in python. Then my flask app will take the output, process it again, and return it to the user.
I am able to run the whole workflow on my local Linux system perfectly, I open the HTML form, fill the input and submit, then the browser redirects me to the result page with no problem at all. But when I try to deploy this whole pipeline on a word press server our lab uses, I can't make it work. I can still run the flask app but it does not listen to the post request on the HTML form at all. I have attached my flask app and HTML code. Is there something I wasn't setting right? Or is there some kind of system that I can just use it to set the whole thing locally and deploy the pipeline onto the server all at once? I have been looking through python flask deployment suggestion but most of them just get me very confused. It would be great if anyone can give me some helpful advice on deploying this pipeline onto the server. Any advice is appreciated!
<form action = "http://localhost:5000/login" method = "POST"
enctype="multipart/form-data">
<fieldset>
<legend>Plot Config:</legend>
<br>Please upload your input file:<br>
<input type="file" name="file" accept="*/*">
<br><br>
<br>Classifer type:<br>
<input type="radio" name="classifier" value="SVM" checked>Support Vector Machine<br>
<input type="radio" name="classifier" value="NB">Naive Bayes<br>
<input type="radio" name="classifier" value="RF">Random Forest<br>
<br>Number of estimators (if you chose Random Forest as your classifier):<br>
<input type="text" name="estimators" value="10"><br>
<br>Would you like to average your result?:<br>
<input type="radio" name="avg" value="Yes" checked>Yes<br>
<input type="radio" name="avg" value="No"> No<br>
<br>Feature Selection interval:<br>
<input type="text" name="interval" value=10><br>
<br>Plot feature range:<br>
<input type="text" name="plotrange" value=85><br>
<br>Plot lengend size:<br>
<input type="text" name="legendsize" value=7.5><br>
<br>Plot line width:<br>
<input type="text" name="plotlinewidth" value=2><br>
<br>Legend title:<br>
<input type="text" name="legendtitle"><br>
<br><br>
<input type="reset">
<input type="submit" value="Submit">
</fieldset>
</form>
from flask import Flask, redirect, url_for, request,send_file
import configparser
from werkzeug import secure_filename
import os
from subprocess import call
app = Flask(__name__)
#app.route('/success/<name>')
def success(name):
return 'welcome! %s' % name
#app.route('/get_image')
def get_image():
if request.args.get('type') == '1':
filename = 'download.jpg'
else:
filename = 'download.jpg'
return send_file(filename, mimetype='image/jpg')
#app.route('/login',methods = ['POST', 'GET'])
def login():
if request.method == 'POST':
f=request.files['file']
workdir=os.path.join(os.getcwd(), 'Input(LeaveOneOut)',f.filename)
print workdir
f.save(workdir)
classifier = request.form['classifier']
estimators=request.form['estimators']
avg=request.form['avg']
interval=request.form['interval']
plotrange=request.form['plotrange']
legendsize=request.form['legendsize']
plotlinewidth=request.form['plotlinewidth']
legendtitle=request.form['legendtitle']
testoutput=classifier+" "+estimators+" "+avg+" "+interval+" "+plotrange+" "+legendsize+" "+plotlinewidth+" "+legendtitle
settings = configparser.ConfigParser()
settings._interpolation = configparser.ExtendedInterpolation()
settings.read('LeaveOneOutConfig.txt')
settings.set('SectionOne', 'Classifier', str(classifier))
settings.set('SectionOne', 'number of estimators', str(estimators))
settings.set('SectionOne', 'average the result', str(avg))
settings.set('SectionOne', 'feature selection interval', str(interval))
settings.set('SectionOne', 'plot feature range', str(plotrange))
settings.set('SectionOne', 'plot lengend size', str(legendsize))
settings.set('SectionOne', 'plot line width', str(plotlinewidth))
settings.set('SectionOne', 'dataset type name', str(legendtitle))
call(["python", "LeaveOneOut.py"])
with open('LeaveOneOutConfig.txt', 'wb') as configfile:
settings.write(configfile)
return redirect(url_for('get_image'))
return redirect(url_for('success',name =testoutput ))
if __name__ == '__main__':
app.run(debug = True)
So I eventually figured out that if you would like the content to be public, you would need to make the server run at 0.0.0.0 and port 80.
In my case I changed the last line to app.run("0.0.0.0", port=80). Also, the flask attached server seems to be quite unstable, this is actually written in the flask documentation that you should use some other deployment options. I used gunicorn which is easy to use and has been stable for me. Hope it helps if anyone runs into the same problem.

How to receive uploaded file with Klein like Flask in python

When setting up a Flask server, we can try to receive the file user uploaded by
imagefile = flask.request.files['imagefile']
filename_ = str(datetime.datetime.now()).replace(' ', '_') + \
werkzeug.secure_filename(imagefile.filename)
filename = os.path.join(UPLOAD_FOLDER, filename_)
imagefile.save(filename)
logging.info('Saving to %s.', filename)
image = exifutil.open_oriented_im(filename)
When I am looking at the Klein documentation, I've seen http://klein.readthedocs.io/en/latest/examples/staticfiles.html, however this seems like providing file from the webservice instead of receiving a file that's been uploaded to the web service. If I want to let my Klein server able to receive an abc.jpg and save it in the file system, is there any documentation that can guide me towards that objective?
As Liam Kelly commented, the snippets from this post should work. Using cgi.FieldStorage makes it possible to easily send file metadata without explicitly sending it. A Klein/Twisted approach would look something like this:
from cgi import FieldStorage
from klein import Klein
from werkzeug import secure_filename
app = Klein()
#app.route('/')
def formpage(request):
return '''
<form action="/images" enctype="multipart/form-data" method="post">
<p>
Please specify a file, or a set of files:<br>
<input type="file" name="datafile" size="40">
</p>
<div>
<input type="submit" value="Send">
</div>
</form>
'''
#app.route('/images', methods=['POST'])
def processImages(request):
method = request.method.decode('utf-8').upper()
content_type = request.getHeader('content-type')
img = FieldStorage(
fp = request.content,
headers = request.getAllHeaders(),
environ = {'REQUEST_METHOD': method, 'CONTENT_TYPE': content_type})
name = secure_filename(img[b'datafile'].filename)
with open(name, 'wb') as fileOutput:
# fileOutput.write(img['datafile'].value)
fileOutput.write(request.args[b'datafile'][0])
app.run('localhost', 8000)
For whatever reason, my Python 3.4 (Ubuntu 14.04) version of cgi.FieldStorage doesn't return the correct results. I tested this on Python 2.7.11 and it works fine. With that being said, you could also collect the filename and other metadata on the frontend and send them in an ajax call to klein. This way you won't have to do too much processing on the backend (which is usually a good thing). Alternatively, you could figure out how to use the utilities provided by werkzeug. The functions werkzeug.secure_filename and request.files (ie. FileStorage) aren't particularly difficult to implement or recreate.

How to use BaseHTTPServer to serve a form in a self-contained server?

I have a simple web server based on BaseHTTPServer which processes GET requests (I reuse a previous example below). I would like, for a particular GET parameter (x in the example below), to open a web page with a simple form and a submit button.
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from urlparse import urlparse, parse_qs
class GetHandler(BaseHTTPRequestHandler):
def do_GET(self):
url = urlparse(self.path)
d = parse_qs(url[4])
if 'x' in d:
self.handle_input_form() # this is the part I want to write
else:
if 'c' in d:
print d['c'][0]
self.send_response(200)
self.end_headers()
return
def handle_input_form(self):
# here a form is displayed, user can input something, submit and
# the content this is handled back to the script for processing
# the next three lines are just a place holder
pass
self.send_response(200)
self.end_headers()
server = HTTPServer(('localhost', 8080), GetHandler)
server.serve_forever()
In other words, this is a self-contained web server (no cgi pages) which I want to keep as simple as possible. I saw a great example of how to use CGI together with the documentation pages, but all assume that there will be a classical cgi-bin structure.
Is what I am trying to achieve easy in Python (I am sure it is possible :))?
I would appreciate very much a general answer on best practices ("do it like that" or "don't do it like that" - please keep in mind this is an internal, private server which will not run anything important) as well as the overall flow of handle_input_form().
EDIT: Following up on Jon Clements' suggestion I used Bottle and adapted the example in the tutorial:
import bottle
#bottle.get('/note') # or #route('/note')
def note():
return '''
<form action="/note" method="post">
<textarea cols="40" rows="5" name="note">
Some initial text, if needed
</textarea>
<input value="submit" type="submit" />
</form>
'''
#bottle.post('/note') # or #route('/note', method='POST')
def note_update():
note = bottle.request.forms.get('note')
# processing the content of the text frame here
For something this simple that's also scalable you're better off using a micro-framework such as flask or bottle.

Categories

Resources