Python - Flask - MatPlotLib - python

I am trying to embed a plot from a function that I pass into my flask application.
The below does everything fine but launches the plot outside of the browser.
I've read a lot about IO and all but I'm confused on how/where to handle that.
The function runBB() in app.py returns plt.show(). After being passed symbol which is retrieved from the form. Should I be passing fig instead to do the IO calculations in app.py or should I do that in the file that holds the function?
I don't want to save the file anywhere as I want this to be dynamic from the stand point of multiple not overwriting the saved file which is what IO gets around I believe.
app.py
#Dashboard page
#app.route("/dashboard", methods = ["POST", "GET"])
def dashboard():
if person["is_logged_in"] == True:
return render_template("dashboard.html", email = person["email"], name = person["name"])
else:
return redirect(url_for('login'))
#app.route("/form", methods = ["POST", "GET"])
def form():
if request.method == "POST":
symbol = request.form["symbol"]
bytes_obj = runBB(symbol)
return render_template("dashboard.html", symbol=bytes_obj)
dashboard.html
{% extends "layout.html" %}
{% block content %}
<head>
<title>Welcome</title>
<link rel="stylesheet" type="text/css" href="{{url_for('static', filename = 'dashboard.css')}}">
<link rel="stylesheet" type="text/css" href="../static/dashboard.css">
</head>
<body>
<div class="main">
<h1 class="name">Hi, {{name}}</h1>
<form action="{{ url_for('form') }}" method ="POST">
<p><input placeholder="Enter your symbol" type="text" name="symbol"></p>
<p><input type="submit" value="Run Report" class="btn-default"></p>
</form>
<P>{{bytes_obj}}</P>
<!-- <hr style="width: 30%"> -->
<h3 class="email">{{email}}</h3>
</div>
</body>
{% endblock %}
Can someone point me in the right direction?
I've tried to comprehend other similar stackoverflow questions and can't seem to grasp this or put it together.

Please refer to this link to learn how to do this using:
import io
import base64
https://gitlab.com/snippets/1924163

Related

Nonetype error while saving file with dropzone js in Django

I am trying to save documents/images into my SQL database using dropzone.js ,for my web app made using Django
HTML FILE:
{% extends 'Main/logged_base_expert.html' %}
{% block content %}
{% load static %}
<head>
<script src="https://rawgit.com/enyo/dropzone/master/dist/dropzone.js"></script>
<link rel="stylesheet" href="https://rawgit.com/enyo/dropzone/master/dist/dropzone.css">
</head>
<body>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.5/css/bootstrap-select.min.css" media="screen">
<form action="{% url 'document_upload' %}" method = "POST" class="dropzone dz" role="form" enctype="multipart/form-data">
{% csrf_token %}
<div class="fallback">
<input name="file" type="file" id="file" multiple >
</div>
<button type="submit" class="form-control" id="cf-submit" name="submit">Submit</button>
</form>
<!-- client section end -->
<!-- footer section start -->
{% endblock %}
views.py :
def document_upload(request):
c = main.objects.get(username = request.session['username'])
unq_id = c.unq_id
print("overall")
if request.method == 'POST':
images = request.FILES.get('file')
print("image")
fs = FileSystemStorage()
filename = fs.save(images.name, images)
Doc1 = Doc(user_id = unq_id, upload = images)
Doc1.save()
return render(request, 'Main/document_upload.html')
models.py:
class Doc(models.Model):
unq_id = models.AutoField(primary_key=True)
user_id = models.BigIntegerField()
upload = models.ImageField(upload_to= 'expert/images')
def __str__(self):
return str(self.unq_id)
The issue I keep facing is that , i keep getting the error
AttributeError at /document_upload/
'NoneType' object has no attribute 'name'
It occurs in the following line, in views.py:
filename = fs.save(images.name, images)
Additional Info : the relative path of the image seems to be storing just perfectly in the database, but since i keep getting the error , i am unable to redirect the app to another page
you're getting AttributeError because this couldn't be found in your request - request.FILES.get('file'). Print request.FILES and see what you have in there.

Bokeh scatter plots not showing

I tried to make a flask app that gets a CSV file from the user, then fits my ML algorithm and finally shows two scatter plots.
Although I could show individual scatter plots via Markup(file_html(plot_obj, CDN, "my plot"), when I use components(plot_obj), and render_template to show both of them together it does not show any of them. Also, I have no error in the console.
I am able to upload the data, then run my algorithm and get the results in the console, however I still don't see the plots.
here is my app.py:
from prad_run import fit
from plots_anomalies import tsne_plot
from bokeh.resources import INLINE, CDN
from bokeh.plotting import figure
import os
from flask import Flask, flash, request, redirect, render_template
from pr_run import fit
from plots import tsne_plot
app = Flask(__name__)
#app.route('/',methods = ['GET'])
def home():
return render_template('home.html')
#app.route('/', methods=['POST'])
def results():
if request.method == 'POST':
file = request.files['file']
df = pd.read_csv(file)
gold_label, df_pr = fit(df)
y_pr = df_pr.loc[:, 'avg_score']
X_tsne = df_pr.drop(columns='avg_score')
tsne_value = plot(X_tsne)
p_true = figure(title="Data with True Labels",plot_width=300, plot_height=300,)
true_df = pd.DataFrame(data=tsne_value, columns=["v1", "v2"])
colormap = {1: 'red', 0: 'green'}
true_df['label'] = gold_label
true_df["color"] = true_df['label'].map(lambda x: colormap[x])
p_true.scatter(true_df['v1'], true_df['v2'], color=true_df['color'], fill_alpha=0.8, size=5)
p_pr = figure(title="Predicted results",plot_width=300, plot_height=300)
tsne_df = pd.DataFrame(data=tsne_value, columns=["v1", "v2"])
tsne_df["pr"] = [1 if x > 0.115 else 0 for x in y_pr]
colormap = {1: 'red', 0: 'green'}
tsne_df['color'] = tsne_df['pr'].map(lambda x: colormap[x])
print(tsne_df['color'].values)
p_pr.scatter(tsne_df['v1'], tsne_df['v2'], color=tsne_df['color'], fill_alpha=0.8, size=7)
js1, divs1 = components(p_true)
js2, divs2 = components(p_pr)
return render_template("home.html", js1=js1, divs1=divs1,js2=js2, divs2=divs2 )
In my home.html:
<!doctype html>
<html>
<title>Python Flask File Upload Example</title>
<h2>Select a file to upload</h2>
<p>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</p>
<form method="post" action="/" enctype="multipart/form-data">
<dl>
<p>
<input type="file" name="file" autocomplete="off" required>
</p>
</dl>
<p>
<input type="submit" value="Submit">
</p>
</form>
<meta charset="UTF-8">
<head>
<title>Figure examples</title>
<link rel="stylesheet" href="http://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.css" type="text/css" />
<script type="text/javascript" src="http://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.js"></script>
<link
href="https://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.css"
rel="stylesheet" type="text/css">
<link
href="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.4.0.min.css"
rel="stylesheet" type="text/css">
<link
href="https://cdn.bokeh.org/bokeh/release/bokeh-tables-1.4.0.min.css"
rel="stylesheet" type="text/css">
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.4.0.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-1.4.0.min.js"></script>
{{ script|safe }}
</head>
<body>
<div style="width: 20%; display: inline-block;">
{{ divs1 | safe }}
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.4.0.min.js"></script>
{{ js1 | safe }}
</div>
<div style="width: 20%; display: inline-block;">
{{ divs2 | safe }}
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.4.0.min.js"></script>
{{ js2 | safe }}
</div>
</body>
</html>
Any idea what is causing this problem?
The <script> tags returned by components have to go in the <head> not <body>. Additionally, you don't need to load BokehJS from CDN multiple times as you are doing above.
You might also want to consider the newer json_items API for embedding, which can be simpler in many cases:
https://docs.bokeh.org/en/latest/docs/user_guide/embed.html?highlight=components#json-items
I found out that the reason that my plots did not display was using Conda environment. If you use Bokeh and Flask I encourage you to use virtualenv in the same folder that you are making your app instead of creating Conda environment.

How to handle errors in Django

I want to make my django app as user friendly as possible and I want to handle appropriate errors and have it push out an error message sort of like an alert in javascript. I want to do this when there's no file uploaded. So when the upload button is pressed and nothing have been uploaded there would be an alert message sent out.
My view, views.py:
def upload(request):
if "GET" == request.method:
return render(request, 'uploadpage/upload.html', {})
else:
excel_file = request.FILES["excel_file"]
# you may put validations here to check extension or file size
wb = openpyxl.load_workbook(excel_file)
# getting a particular sheet by name out of many sheets
worksheet = wb['Summary']
# iterating over the rows and
# getting value from each cell in row
seller_info = []
for cells in worksheet.iter_rows(min_col=2, max_col=2, min_row=1, max_row=5):
for cell in cells:
seller_info.append(str(cell.value))
return render(request, 'uploadpage/upload.html', {"excel_data": seller_info})
My template, uploadpage/upload.html:
<!DOCTYPE html>
<html>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="{% static 'css/upload.css' %}">
<head>
<div id='banner-container'>
<div id='banner'>
<h1 id='header'>MY APP</h1>
<i class="glyphicon glyphicon-cloud" style="font-size:60px;color:lightblue;text-shadow:2px 2px 4px #000000;"></i>
</div>
<div>
<body>
<div id='upload-container' >
<span><h1>Upload File !</h1></span>
<span><h2>Upload Here</h2></span>
<form method="post" enctype="multipart/form-data">
<div id='input'>
{% csrf_token %}
<input type="file" name="excel_file">
<div id='btn'><button type="submit">Upload File</button> </div>
</form>
<div>
</div>
</body>
{{ excel_data }}
</head>
</html>
Django provided us a message framework which allow you to attach messages and then you can render it on your template by using JavaScript or simply just use django template.
My favorite library to show message on my web application is toastr. You can go to the document page to see how you will integrate into your project.
On your views:
from django.contrib import messages
# ...
def upload(request):
if "GET" == request.method:
messages.error(request, "There's no file uploaded")
return render(request, 'uploadpage/upload.html', {})
# ...
Then on your template you can use it like so:
...
<head>
...
<link href="toastr.min.css" rel="stylesheet" />
</head>
<body>
...
<script src="toastr.min.js"></script>
{% if messages %}
<script>
toastr.options = {
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000"
}
{% for message in messages %}
toastr.{{ message.tags }}("{{ message }}");
{% endfor %}
</script>
{% endif %}
</body>
message.tags: Using to match with the function of toastr, for example if you want to show an error by using messages.error(...) then the message.tags will be error, when your template rendered it turned to toastr.error("Your message here") and then you'll see the toast message on your browser.
Hope that helps!
There's two ways to go about it:
You can create a client-side check that prevents the form from being sent with Javascript. That is not Django-specific and you'll have no trouble finding examples.
You catch the fact that no file was sent and set an extra flag for the upload.html template. Untested example code:
message = None
data = None
if request.FILES:
data = # process file
message = "Upload successful!"
else:
message = "Please upload a file!"
return render(request, 'upload.html', {"data": data, "message": message})
Then you can show the message in the template:
{% if message %}
<div class="message">{{message}}</div>
{% endif %}

List generated from class is not available returning render_template in flask

I'm very new to Python and Flask and I can't figure out why I don't have the list of data available to me here (ranked_list is empty).
If I comment out all the Flask parts and just call getDataFromQuery to get the data like a normal script, I can see the data and print it out. Can anyone see what I'm doing wrong? The data is a list of tuples. The index.html page is below the code and it is in the templates folder. All I get is a blank table with the header row.
from flask import Flask
from flask import render_template
from flask import request
from queryProcessing_and_Ranking import *
app = Flask(__name__)
#app.route("/")
#app.route("/<query>")
def index():
query = request.args.get("query")
Processing = QueryProcessing()
ranked_list = Processing.getDataFromQuery( query )
return render_template( "index.html", ranked_list = ranked_list, user_input = query )
if __name__ == '__main__':
port = int( os.environ.get('PORT', 5000 ) )
app.run( host='0.0.0.0', port=port, debug=True)
<html>
<head>
<title>Product Vertical Search Engine</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<form>
Product Search: <input type="text" name="query"> <input type="Submit" value="Search">
</form>
<h1>Ranked product listing for: {{user_input}}</h1>
<table border = "1">
<tr>
<th>Title</th><th>Price</th>
</tr>
{% for item in ranked_list %}
<tr>
<td>{{item[0]}}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
Given your route setup query is likely None and thus you are passing None to your .getDataFromQuery method.
#app.route('/')
def index():
'''
Returns "None" if not found.
So when you open your browser to localhost:5000
this value is None
unless you visit localhost:5000/something
then query = "something"
'''
query = request.args.get('query')
Processing = QueryProcessing()
ranked_list = Processing.getDataFromQuery(query) # Value could be None
return render_template( "index.html", ranked_list = ranked_list, user_input = query )
You should also remove your route definition that captures <query> as it looks like you are mixing up the concept of path parameters and query string parameters
EDIT
This looks like what you are trying to do is search on a form submission so I'd do the following
#app.route('/')
def index():
user_input = None
ranked_list = None
if request.method == 'POST':
user_input = request.form['query']
Processing = QueryProcessing()
ranked_list = Processing.getDataFromQuery(user_input)
return render_template("index.html", ranked_list=ranked_list, user_input=user_input)
HTML file
<html>
<head>
<title>Product Vertical Search Engine</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<form>
Product Search: <input type="text" name="query"> <input type="Submit" value="Search">
</form>
{% if user_input %} <!-- See if you have searched yet -->
<h1>Ranked product listing for: {{user_input}}</h1>
<table border = "1">
<tr>
<th>Title</th><th>Price</th>
</tr>
{% for item in ranked_list %}
<tr>
<td>{{item[0]}}</td>
</tr>
{% endfor %}
</table>
{% else %} <!-- tell user to search for data or that there is no data -->
<h1>Search for data</h1>
{% endif %}
</body>
</html>
Use some Jinja2 logic to explicitly state that nothing is there for a bit better feedback

Flask and WTForms with custom validation

I am creating an online form with Python, to send an online form. The form consists of a mixture of free input fields, and standard options. What it does now is convert the input to a mail, and send it. That's great, but I would like to build in a functionality that checks the input first. I need the length of two different inputfields to be of the size. So if someone enters 4 products and only 3 quantities, I want it to return a warning that these amounts differ.
base.py:
from flask import *
from wtforms import *
import yagmail
yag = yagmail.SMTP('email', 'pass')
# App config.
DEBUG = True
app = Flask(__name__)
app.config.from_object(__name__)
app.config['SECRET_KEY'] = 'key'
class ReusableForm(Form):
naam = TextField('Name:', validators=[validators.required()])
#app.route("/", methods=['GET', 'POST'])
def index():
form = ReusableForm(request.form)
print form.errors
if request.method == 'POST':
# Process all input
naam=request.form['naam']
productcodes=request.form['productcodes']
productquantity=request.form['productquantity']
# Convert SKU & EAN input to list of entries
productcodes = [int(i) for i in productcodes.strip('{}').split('\n')]
productquantity = [int(i) for i in productquantity.strip('{}').split('\n')]
# tried this; didn't work
# if len(productcodes) != len(productquantity):
# flash('Unequal inputs')
if form.validate():
# Comment when form is validates
flash('Order succesvol: ' + naam)
(send mail)
else:
flash('Error: All the form fields are required. ')
return render_template('hello.html', form=form)
if __name__ == "__main__":
app.run()
hello.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Form</title>
<link rel="stylesheet" media="screen" href ="static/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">
</head>
<body>
<div class="container">
<h2>Form</h2>
<form action="" method="post" role="form">
{{ form.csrf }}
<div class="form-group">
<label for="naam">Naam:</label>
<select name="naam" class="selectpicker form-control">
<option value="Jack">Jack</option>
<option value="John">John</option>
</select>
<br>
<label for="productcodes">SKU-codes:</label>
<textarea class="form-control" id="productcodes" name="productcodes"></textarea>
<br>
<textarea class="form-control" id="productquantity" name="productquantity"></textarea>
<br>
</div>
<button type="submit" class="btn btn-success">Send</button>
</form>
<br>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for message in messages %}
{% if "Error" not in message[1]: %}
<div class="alert alert-info">
<strong>Success! </strong> {{ message[1] }}
</div>
{% endif %}
{% if "Error" in message[1]: %}
<div class="alert alert-warning">
{{ message[1] }}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
</div>
<br>
</div>
</div>
</body>
</html>
So what i would like to have is that upon the first click, the check is executed. If the size of the input of productcodes is smaller than the size of productquantity, the user has to edit the inputs. If not, the user has to confirm and is able to send the mail.
The lines that are commented out in base.py didn't work.
Thanks in advance!
Quick and Dirty
Based on the commented-out code, in your original attempt there was nothing stopping the form from validating and submitting. Your if len(productcodes) != len(productquantity) just flashed a message, which only shows on the next page load, but doesn't stop validation of the form. form.validate() can only validate the fields you tell it about.
Change the check to be something like:
if len(productcodes) != len(productquantity) and form.validate():
That way your basic check will bounce to the failed validation code path.
The "Right" Way
Keep in mind, even if that works, you're still basing your check on parsing text from a <textarea>, with little to enforce the format. Any unexpected input is likely to break it.
I'd strongly recommend you rethink your form to be a bit more in line with how WTForms and Flask work. Each form field should map directly to an attribute of your Form subclass. That way you can use the built-in validators, and not have to create your own. You should probably look at using something other than a raw <textarea> as well. It may be a bit more work up-front, but it will make it a lot easier for you and your users down the road.

Categories

Resources