I am very much struggling with a concept and hope someone can offer a code implementation suggestion. I have the following route embedded within my flask app. After working on this for a few days I think I now see the nature of the issue. I have modified my code based on suggestion below in comments.
In the web app now, the names of the data frame columns populate a drop down menu and the user can choose one of those variables and click "Show". That variable name then prints to the screen just so I know my POST from the form is communicating with my function in the flask app.
What I would like to do is create a temp version of the file uploaded so that it exists as long as the web session is open (or is overwritten if a new file upload occurs). Then, the user chooses a variable from the drop down menu and the mean of that variable is computed (that part is commented out in the code below for now but you can see what I have tried).
If the user reads in a new file, then the same process would occur on that new file.
Thanks for any suggestions or supports.
from flask import Flask, render_template, request
import numpy as np
from scipy.stats import binom
from scipy.optimize import minimize
from scipy.stats import norm
from scipy import optimize
from pyodbc import connect
import pandas as pd
import os
import tempfile
app = Flask(__name__)
#app.route('/dataTools', methods=['POST', 'GET'])
def data_tools_upload():
tempfile_path = tempfile.NamedTemporaryFile().name
if request.files:
df = pd.read_csv(request.files.get('file'))
#tempfile_path = tempfile.NamedTemporaryFile().name
df.to_csv(tempfile_path)
if os.path.exists(tempfile_path):
orig_df = pd.read_csv(tempfile_path)
vars = list(orig_df.columns)
var2use = request.form.get("var2use")
#indx = vars.index(var2use)
#df[vars[indx]].mean()
mean = orig_df[vars[4]].mean()
dims = orig_df.shape
message = 'You have data! There are %s rows and %s columns and the variable %s has mean %s' % (dims[0],dims[1],vars[4],round(mean,3))
table = orig_df.head(10).to_html(classes='data', header = "true")
return render_template('upload.html', tables = [table], message = message, vars = vars, var_name = var2use)
var2use = request.form.get("var2use")
return render_template('upload.html', var_name = var2use, message = "What happened?")
if __name__ == '__main__':
app.run(debug=True)
And then the following HTML bit (upload.html):
{% extends "layout.html" %}
{% block body %}
<h3> Read in a data file </h3>
<br>
<form method=post enctype=multipart/form-data>
<input type=file name=file class = "btn btn-outline-secondary">
<input type=submit value=Upload class = "btn btn-outline-secondary">
</form>
<br>
<form class="form-inline" action = "{{url_for('data_tools_upload')}}" method = "POST">
<select type = "text" name="var2use" class="custom-select mr-sm-1">
{% for var in vars %}
<option value= "{{ var }}" SELECTED>{{ var }}</option>"
{% endfor %}
</select>
<button class = "btn btn-primary"> Show </button>
</form>
<center>
<h1>
{{message}}
{{name}}
</h1>
<br>
<small>
{% for table in tables %}
{{ table|safe }}
{% endfor %}
{% endblock %}
</small>
</center>
you need to save file to work with it (the code works perfectly in my app):
f = request.files['file']
tmp_filename = '/tmp/my_tmp_file.csv'
f.save(tmp_filename)
pd.read_csv(tmp_filename)
Here is the detailed documentation on Flask file uploads:
https://flask.palletsprojects.com/en/1.1.x/patterns/fileuploads/
Full example for your case is below. I think you might have problems with the following three items:
Uploading the file because of form enctype (should be "multipart/form-data")
Uploading because of file size (try my sample)
Processing the form with empty file overwriting yours (see my example for "csv_file_uploaded")
Python (csv_test.py):
from flask import Flask, render_template, request
import pandas as pd
import os
app = Flask(__name__, static_url_path='')
#app.route('/', methods=['GET', 'POST'])
def index():
data = {}
tmp_filename = '/tmp/csv_test.csv'
if request.files:
csv_file_uploaded = request.files.get('file')
if csv_file_uploaded:
f = request.files['file']
f.save(tmp_filename)
if os.path.exists(tmp_filename):
df = pd.read_csv(tmp_filename)
data['columns'] = ', '.join(df.columns)
col_selected = request.form.get('col_selected')
print(col_selected)
data['col_selected'] = col_selected
if col_selected:
mean = df.get(col_selected).mean()
data['mean'] = mean
return render_template('csv_test.tpl', data=data)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
Flask template (csv_test.tpl):
<html><head><title>CSV TEST</title></head>
<body><h1>CSV TEST</h1>
{% if data %}
<div>Columns of the uploaded file: <strong>{{ data.columns }}</strong></div>
<div>Selected column: <strong>{{ data.col_selected }}</strong></div>
<div>Mean of the selected = <strong>{{ data.mean }}</strong></div>
{% endif %}
<fieldset>
<form enctype="multipart/form-data" action="" method="post">
<h2>Provide some CSV with first column in numbers and/or column name</h2>
<div>File{% if data.columns %} (UPLOADED){% endif %}: <input id="file" type="file" name="file"/></div>
<div>Column: <input id="col_selected" type="text" name="col_selected" value="{{ data.col_selected }}"/></div>
<div><input id="submit" type="submit" value="SUBMIT"/></div>
</form>
</fieldset>
</body></html>
CSV file (123.csv) - use it to test the form:
col1,col2
1,2
2,3
3,4
5,6
Related
This question already has answers here:
Create and download a CSV file from a Flask view
(3 answers)
Closed 2 years ago.
My goal is to run my data de-identifying script and download my de-identified data from Flask. Right now, I have created a page to upload my file onto my web and I want to execute my de-identifying script using the execute button on my HTML and download the file.
My HTML:
{% block title %}Upload{% endblock %}
{% block main %}
<div class="container">
<div class="row">
<div class="col">
<h1>Upload the file</h1>
<hr>
<form action="/upload-file" method="POST" enctype="multipart/form-data">
<div class="form-group">
<label>Select file</label>
<div class="custom-file">
<input type="file" class="custom-file-input" name="Dataset" id="Dataset">
<label class="custom-file-label" for="Dataset">Select file...</label>
</div>
</div>
<button type="submit" class="btn btn-primary">De-indentify</button>
</form>
<form action="/upload-file" method="GET" enctype="multipart/form-data">
<button type="submit" class="btn btn-primary">Execute</button>
</form>
</div>
</div>
</div>
{% endblock %}
My App Flask route:
app.config["FILE_UPLOADS"] = "app/app/static/csv/uploads"
app.config["ALLOWED_FILE_EXTENSIONS"] = ["csv"]
def allowed_file(filename):
# We only want files with a . in the filename
if not "." in filename:
return False
# Split the extension from the filename
ext = filename.rsplit(".", 1)[1]
# Check if the extension is in ALLOWED_IMAGE_EXTENSIONS
if ext.upper() in app.config["ALLOWED_FILE_EXTENSIONS"]:
return True
else:
return False
#app.route("/upload-file", methods=["GET", "POST"])
def upload_file():
if request.method == "POST":
if request.files:
Dataset = request.files["Dataset"]
if Dataset.filename == "":
print("File must have a filename ")
return redirect(request.url)
if allowed_file(Dataset.filename):
print("That file extension is not allowed")
return redirect(request.url)
else:
filename = secure_filename(Dataset.filename)
Dataset.save(os.path.join(
app.config["FILE_UPLOADS"], filename))
print("Dataset saved")
return redirect(request.url)
return render_template("public/upload_file.html")
The file that I have uploaded:
Housing,Houseprice,Name,Neighbourhood,State
123556,100000,John,Bloomingdale,Washington
111777,250000,Ian,Bloomingdale,Washington
998273,250000,Tom,Spring Valley,California
My de-identifying script:
import pandas as pd
import uuid as u
# generate a pseudo-identifier sequesnce using python random number generator library uudi.
def uudi_generator(length):
uudi_list= list()
i=0
while i < length:
uudi_list.append(u.uuid4())
i+=1
return uudi_list
#import dataset
dataset = pd.read_csv('C:\\mylocation\\housing.csv', index_col=False)
# pseudo identifier
sLength = len(dataset['Housing'])
dataset.insert(0, 'uuid', pd.Series(uudi_generator(sLength), index=dataset.index))
#delete identifiabLe record from dataset
del dataset['Name']
del dataset['Neigbourhood']
Try this:
from flask import Response
csv_text = ""
for row in dataset.values:
csv_text += f"{row[0]},{row[1]},{row[2]},{row[3]}\n"
return Response(csv_text, mimetype='text/csv')
You don't even need to del the unwanted columns. Just don't add them in csv_text. You don't really even need pandas. You could read the csv as a text file, split('\n') and add the uuids when you compile the csv_text
This is my first attempt at deploying a machine learning application and also my first time using flask. Essentially the user will fill out a form on an html page and the input from the form will be used as input to the machine learning model which is saved in a pickle file, model.pkl in the code below. I am running into one snag that I can't seem to break past..
Every time I submit from index.html and post to result.html I'm receiving a 404 error.
script.py:
#importing libraries
import os
import numpy as np
import flask
import pickle
from flask import Flask, render_template, request
app=Flask(__name__)
#app.route('/')
#app.route('/index')
def index():
return flask.render_template('index.html')
def ValuePredictor(to_predict_list):
to_predict = np.array(to_predict_list).reshape(1,12)
loaded_model = pickle.load(open("model.pkl","rb"))
result = loaded_model.predict(to_predict)
return result[0]
#app.route('/result',methods = ['POST'])
def result():
if request.method == 'POST':
to_predict_list = request.form.to_dict()
to_predict_list=list(to_predict_list.values())
to_predict_list = list(map(int, to_predict_list))
result = ValuePredictor(to_predict_list)
if int(result)==1:
prediction='Income more than 50K'
else:
prediction='Income less that 50K'
return render_template("result.html",prediction=prediction)
index.html:
<html>
<body>
<h3>Income Prediction Form</h3>
<div>
<form action="/result.html" method="POST">
<label for="age">Age</label>
<input type="text" id="age" name="age">
<br>
<label for="edu">Education</label>
<select id="edu" name="edu">
<option value="0">High School</option>
<option value="1">College Degree</option>
</select>
<br>
<label for="martial_stat">Marital Status</label>
<select id="martial_stat" name="martial_stat">
<option value="0">not married</option>
<option value="1">married</option>
</select>
<br>
<label for="gender">Gender</label>
<select id="gender" name="gender">
<option value="0">Female</option>
<option value="1">Male</option>
</select>
<br>
<input type="submit" value="Submit">
</form>
</div>
</body>
</html>
result.html:
<html>
<body>
<h1> {{ prediction }}</h1>
</body>
</html>
I can't seem to figure this out. My code never seems to reach the first line in the result() function. As soon as I submit from index.html http://127.0.0.1:5000/result.html throws a 404 error. Any suggestions?
The error is very simple here, the action property in index.html should just be
<form action="/result" method="POST">
instead of
<form action="/result.html" method="POST">
You want to use /result to go through your flask function. Hope this helps!
You are posting to the endpoint /result.html:
<form action="/result.html" method="POST">
... but your route in flask is defined as /result (with no .html):
#app.route('/result',methods = ['POST'])
def result():
...
These two need to match, so consider changing the route to result.html.
Instead of <form action="/result.html" method="POST">
Can Use <form action="{{ url_for('result') }}" method="POST">
So, I am trying to open an .csv file in Python using Flask. I copies the code from the Python library, but I go from one error message to the other and I don't know what I am doing wrong. The latest error code I get on the below code is: TypeError: invalid file:
Any ideas what I am doing wrong?
My Python code/Flash route is as follows:
#app.route("/admin", methods=["GET", "POST"])
#login_required
def admin():
"""Configure Admin Screen"""
# if user reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# load csv file with portfolio data
with open(request.files["portfolios"]) as csvfile:
portfolios = csv.DictReader(csvfile)
# load csv file in dictionary
for row in portfolios:
print(row['first_name'], row['last_name'])
else:
return render_template("admin.html")
My html/Flask code is:
{% extends "layout.html" %}
{% block title %}
Admin
{% endblock %}
{% block main %}
<h2>Admin Console</h2>
<h3> Upload Portfolio Data</h2>
<form action="{{ url_for('admin') }}" method="post" enctype=multipart/form-data>
<fieldset>
<label class="control-label">Select Portfolio Upload File</label>
<input id="input-1" type="file" class="file" name="portfolios">
<h3>Upload Security Lists</h2>
<label class="control-label">Select Security Upload File</label>
<input id="input-1" type="file" class="file" name="securities">
<div class="form-group">
<button class="btn btn-default" type="submit" value = "upload">Upload</button>
</div>
</fieldset>
</form>
{% endblock %}
The file is already open. open takes a string filename and created an open file object, but you don't need to do that because objects in request.files are already open file-like objects.
portfolios = csv.DictReader(request.files['portfolios'])
I have a very simple application that creates, removes, and updates records in a sqlite3 database using Python and Flask. I am currently working on the remove function and have hit a road block. Below is my code for my view that shows the records in the table:
<!doctype html>
<html>
<body>
<table border = 1>
<thead>
<td>Name</td>
<td>Address>/td<
<td>city</td>
<td>Pincode</td>
</thead>
{% for row in rows %}
<tr>
<td>{{row["name"]}}</td>
<td>{{row["addr"]}}</td>
<td> {{ row["city"]}}</td>
<td>{{row['pin']}}</td>
<td><form action = "/foo" method = "POST">
<button id ="w3-btn">delete</button>
</form> </td>
</tr>
{% endfor %}
</table>
Go back to home page
</body>
</html>
As can be seen from the code, I have a delete button for each record displayed. How can I make it such that if the user clicks on the delete button on any given row, that row will be deleted from the table? I would like to know how I can specify the selected row, and how I can send that information/data to 'foo' in my app.py, and how the function in app.py would take that data as an input argument.
If you have row["pin"]
then you can use hidden field in form with method="POST"
<form action="/foo" method="POST">
<input type="hidden" value="{{ row["pin"] }}"/>
<button id="w3-btn">delete</button>
</form>
or using method="GET" and ?id=some_id in url
<form action="/foo?id={{ row["pin"] }}" method="GET">
<button id="w3-btn">delete</button>
</form>
Or even as normal link (which you can style as button using CSS)
delete
In view you will have
# POST
#app.route('/foo', methods=['POST'])
def foo():
pin = requests.form.get('id')
print(pin)
# GET or normal link
#app.route('/foo')
def foo():
pin = requests.args.get('id')
print(pin)
Read doc: http://flask.pocoo.org/docs/0.11/
If you use url without ?id= but "/foo/some_id"
action="/foo/{{ row["pin"] }}"
delete
or using url_for()
action="{{ url_for('foo', pin=row["pin"]) }}"
TEST
then you will need
#app.route('/foo/<pin>')
def foo(pin):
print(pin)
Create a delete view that takes an id to delete.
#app.route('/<int:id>/delete', methods=['POST'])
def delete(id):
r = Record.query.get_or_404(id)
db.session.delete(r)
db.session.commit()
return redirect(url_for('index'))
The delete form's action is the generated url to the delete view.
<form method=post action="{{ url_for('delete', id=r.id) }}">
This assumes that each row has an id attribute. For example:
from flask import Flask, redirect, url_for, render_template_string
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
db.create_all()
db.session.add_all((
Item(name='abc'),
Item(name='def'),
Item(name='ghi'),
))
db.session.commit()
#app.route('/')
def index():
items = Item.query.all()
return render_template_string('''<ul>{% for item in items %}
<li>{{ item.name }} -
<form method=post action="{{ url_for('delete', id=item.id) }}">
<button type=submit>delete</button>
</form></li>
{% endfor %}</ul>''', items=items)
#app.route('/<int:id>/delete')
def delete(id):
item = Item.query.get_or_404(id)
db.session.delete(id)
db.session.commit()
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
I need to build a simple form with a number of checkboxes. The problem is that I need the checkboxes to come from a csv file which would look like this:
data_so.csv
Name
A. Blabla
U. Blublu
I. Blibli
O. Bloblo
The form, right now, with some hard coded checkboxes, looks like this:
Instead of "Mr. 1", I'd need to have "A. Blabla", instead of "Ms. 2", I'd want "U. Blublu", etc. and instead of 3 checkboxes, I'd need 4, the number of entries in my csv file.
Here are my Flask files:
route_so.py
from flask import Flask, render_template, request, flash
from forms_so import ContactForm
import csv
app = Flask(__name__)
app.secret_key = 'development key'
#app.route('/', methods=['GET', 'POST'])
def home():
form = ContactForm()
if request.method == 'POST':
if form.validate() == False:
flash('All fields are required.')
return render_template('home_so.html', form=form)
else:
print(form.node_1.data,form.node_2.data,form.node_3.data)
return render_template('home_so.html', success=True)
elif request.method == 'GET':
return render_template('home_so.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
form_so.py
from flask.ext.wtf import Form
import csv
from wtforms import TextField, RadioField, TextAreaField, SubmitField, validators, BooleanField
class ContactForm(Form):
# my attempt to force the creation of dynamic global variables
with open('/data_so.csv', 'rb') as f:
reader = csv.reader(f)
r = list(reader)
nodes = {}
for i in range(1,len(r)):
globals()[''.join("node_"+str(i))] = BooleanField(r[i][0])
# end of my attempt to force the creation of dynamic global variables
node_1 = BooleanField("Mr. 1")
node_2 = BooleanField("Ms. 2")
node_3 = BooleanField("Dr. 3")
# this needs to be dynamically set instead
submit = SubmitField("Send")
So I tried and created dynamic variables (in a dirty, hacky way). The problem now is that I don't know how to make the home_so.html work with an undifined number of variables...
home_so.html
{% extends "layout_so.html" %}
{% block content %}
{% if success %}
<p>Thank you for filling up our survey. We'll get back to you shortly.</p>
{% else %}
<form action="{{ url_for('home') }}" method=post>
{{ form.hidden_tag() }}
<h2>List of check boxes dynamically built from local csv file</h2>
#this needs to be dynamically set
{{ form.node_1.label }}
{{ form.node_1 }}
{{ form.node_2.label }}
{{ form.node_2 }}
{{ form.node_3.label }}
{{ form.node_3 }}
{{ form.submit }}
</form>
{% endif %}
{% endblock %}
Is there a way to accomplish this sort of things with a simple csv file? If not, what's the usual way to go about dynamically producing a form as it loads client-side?
{% for node in node_list_from_app %}
<p class="field"><label><input type="checkbox" name="node" value="{{ node }}"> {{ node }}</label></p>
{% endfor %}