I am new in Flask. My goal is to generate dynamic div containers every time I upload a dxf file and next submit happens. For example: one file uploaded- one div shown; two files uploaded- two divs shown and so on.
I can convert uploaded dxf files to .png images and I would like to show these images in div elements displayed after every upload.
I use input tag to upload files (type='file') and Java Script to generate dynamic elements (divs and their child tags).
The problem is that every time I upload file, the template is loading again and no new content is shown except the image of the last uploaded dxf. Please, give me a piece of advice to solve it.
HTML
...
<form enctype="multipart/form-data" id="uploadForm" action="/upload_files" name="uploadForm" method="post">
DXF file: <input type="file" id="dxfUpload" onchange="form.submit(); createConfigure();" name="dxfUpload" />
<div id="calcHolder" name="calcHolder">
<script type="text/javascript">
function createConfigure() {
var div = document.createElement("div");
div.id = "dxf-"+Math.random() * 100000000000000000 + "-"
+ window.performance.now() * 100000000000000000;
id_div=div.id;
div.className = 'border pad';
div.style.width = "640px";
div.style.height = "200px";
document.getElementById("calcHolder").appendChild(div);
var img = document.createElement("img");
img.setAttribute("src", "{{url_for('static', filename=dxfName+'.png')}}");
img.setAttribute("alt", "no image");
img.setAttribute("height", "120px");
img.setAttribute("width", "120px");
document.getElementById(id_div).appendChild(img);
var array = ["Carbon Steel","Stainless Steel","Aluminium"];
var selectMaterial = document.createElement("select");
document.getElementById(id_div).appendChild(selectMaterial);
for (var i = 0; i < array.length; i++) {
var option = document.createElement("option");
option.value = array[i];
option.text = array[i];
selectMaterial.appendChild(option);
}
var selectThickness = document.createElement("select");
document.getElementById(id_div).appendChild(selectThickness);
for (i = 1; i <= 16; i++) {
var opt = document.createElement('option');
//opt.value = i;
opt.innerHTML = i + ' mm';
selectThickness.appendChild(opt);
}
var quantity = document.createElement("input")
quantity.type="number";
quantity.value="1";
quantity.name="quantity";
quantity.min="1";
quantity.max="50";
quantity.onkeyup= function maxReach(){if(quantity.value > 50) quantity.value=50;};
document.getElementById(id_div).appendChild(quantity);
var btn = document.createElement("button");
btn.innerHTML = "Delete";
btn.type = "button";
document.getElementById(id_div).appendChild(btn);
btn.onclick = function() {div.remove();};
}
</script>
{{ html | safe }}
</div>
</form>
...
Python
#app.route('/upload_files', methods=['POST'])
def upload_files():
try:
if request.method == 'POST':
dxf_file = request.files['dxfUpload']
full_filename = os.path.join(app.config['UPLOAD_FOLDER'],dxf_file.filename)
dxf_file.save(full_filename)
first = DXF2IMG()
first.convert_dxf2img([full_filename],img_format='.png')
html="<img src="+url_for('static', filename=dxf_file.filename+'.png' )+" width='120' height='120' />"
return render_template('upload_files.html',dxfName=dxf_file.filename, html=html)
except:
...
#something happens
The result now
Desired result
Once the form.submit() function is executed, the form will be sent as a regular post request. For this reason, the following function is no longer executed and the entire page is reloaded.
In order to submit the form and change the content of the existing page, it is necessary to use AJAX.
This example shows you how to submit the form to the server and receive a JSON response containing the URLs of the received file and the generated image.
As soon as the submit button is pressed, the form data is packed into a FormData object and sent via AJAX using the fetch function. The browser's default behavior for a submit event is suppressed and the form is reset. The received file is processed by the server and the associated URLs are sent back to the client in JSON format. Now the document can be changed with the received data.
Remember this is just a minimal example to help you achieve your goals and implement your concept.
Flask (app.py)
import os
import ezdxf
from ezdxf.addons.drawing import matplotlib
from flask import Flask
from flask import (
jsonify,
make_response,
render_template,
url_for
)
from werkzeug.utils import secure_filename
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
def dxf2png(source, target):
doc = ezdxf.readfile(source)
msp = doc.modelspace()
auditor = doc.audit()
if auditor.has_errors:
raise Exception('Conversion failed.')
matplotlib.qsave(doc.modelspace(), target)
#app.route('/upload', methods=['POST'])
def upload():
if 'dxf-file' in request.files:
file = request.files['dxf-file']
if file.filename != '':
filename = secure_filename(file.filename)
filepath = os.path.join(app.static_folder, filename)
destname, _ = os.path.splitext(filename)
destname = f'{destname}.png'
destpath = os.path.join(app.static_folder, destname)
file.save(filepath)
try:
dxf2png(filepath, destpath)
except:
os.remove(filepath)
return make_response('', 400)
return make_response(
jsonify(
target=url_for('static', filename=filename),
preview=url_for('static', filename=destname)
),
200
)
return make_response('', 400)
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
<style media="screen">
.preview {
width: 120px;
height: auto;
}
</style>
</head>
<body>
<form name="dxf-upload" method="post" enctype="multipart/form-data">
<input type="file" name="dxf-file" />
<input type="submit" />
</form>
<div id="dxf-files"></div>
<script type="text/javascript">
((uri) => {
function createPreview(target, preview) {
const divElem = document.createElement('div');
divElem.innerHTML = `<img src="${preview}" class="preview" />`;
const outElem = document.getElementById('dxf-files');
outElem.append(divElem);
}
const form = document.querySelector('form[name="dxf-upload"]');
form.addEventListener('submit', evt => {
evt.preventDefault();
const formData = new FormData(evt.target);
fetch(uri, {
method: 'POST',
body: formData
}).then(resp => resp.json())
.then(data => {
const { target, preview } = data;
createPreview(target, preview);
});
evt.target.reset();
});
})({{ url_for('.upload') | tojson }});
</script>
</body>
</html>
Related
I'm building a Django project in which I need to take a picture from webcam, and then store it in my database and as a file. I'm saving the source in the database, but I'm having some trouble saving it as a file.
Here is my code:
html:
<form method="POST" action="{% url 'takePhoto' %}" enctype="multipart/form-data">
{% csrf_token %}
<video id="video" autoplay ></video>
<canvas id="canvas"></canvas>
<input type="hidden" name="photo" id="photo" value=""/>
<button id="startbutton1" class="btn btn-outline-secondary btn-sm">Take Photo</button>
<button id="submit" type="submit">submit</button>
<script src="{% static 'scripts/script.js' %}"></script>
javascript:
(function() {
var width = 320;
var height = 0;
var streaming = false;
var video = null;
var canvas = null;
var photo = null;
var startbutton1 = null;
function startup() {
video = document.getElementById('video');
canvas = document.getElementById('canvas');
photo = document.getElementById('photo');
startbutton1 = document.getElementById('startbutton1');
navigator.mediaDevices.getUserMedia({video: true, audio: false})
.then(function(stream) {
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.log("An error occurred: " + err);
});
video.addEventListener('canplay', function(ev){
if (!streaming) {
height = video.videoHeight / (video.videoWidth/width);
if (isNaN(height)) {
height = width / (4/3);
}
video.setAttribute('width', width);
video.setAttribute('height', height);
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
streaming = true;
}
}, false);
startbutton1.addEventListener('click', function(ev){
takepicture();
ev.preventDefault();
}, false);
clearphoto();
}
function clearphoto() {
var context = canvas.getContext('2d');
context.fillStyle = "#AAA";
context.fillRect(0, 0, canvas.width, canvas.height);
var data = canvas.toDataURL('image/png');
photo.setAttribute('src', data);
}
function takepicture() {
var context = canvas.getContext('2d');
if (width && height) {
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
var data = canvas.toDataURL('image/png');
photo.value=data;
} else {
clearphoto();
}
}
window.addEventListener('load', startup, false);
})();
views:
def takePhoto(request):
print("works")
if not request.session.get('logged_in'):
return redirect('/appChallenge/login')
if request.method== 'POST':
user = User.objects.get(username=request.session["username"])
img = request.POST.get("photo")
image = img
imagePath="/media"
a=str(img)
image = image.save(f"{imagePath}/{a}.png")
imgInfo= Image(user=user, img=img)
imgInfo.save()
print("works")
return render(request, 'page.html')
When I click submit, it says "'str' object has no attribute 'save'"
Please help. Thanks.
If your js/html code works, you will receive a raw image data encoded to base64 in your view 'takePhoto'. Try
print(img) to see something like "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAADwCAYAA...."
Maybe you will see just "iVBORw0KGgoAAAANSUhEUgAAAUAAAADwCAYAA...." without meta data.
So your 'img' is just a string with base64 encoded value, that hasn't method 'save'.
First, you need to get rid of "data:image/png;base64," in your raw image data, if your image string contains it. It is meta data and base64 could not decode it. You can do it at front end side:
var data = canvas.toDataURL('image/png').replace(/^data:image\/png;base64,/, "")
Or back end:
img = request.POST.get("photo").replace('data:image/png;base64,', '')
Next you need to use django ContentFile to create a file-like object. You need to simulate file with its method .read(). Also you need to decode base64 encoded image data:
import base64
from django.core.files.base import ContentFile
def takePhoto(request):
print("works")
if not request.session.get('logged_in'):
return redirect('/appChallenge/login')
if request.method== 'POST':
user = request.user
# remove meta data from base64 encoded data, you can also
# use 'split(',')[1]' to remove all before ','
img = request.POST.get("photo").replace('data:image/png;base64,', '')
# create a file-like object with your image data
image_file_like = ContentFile(base64.b64decode(img))
# first you need to create object
image = Image(user=user)
# and then save image into your model object
# note: only if your 'img' field is 'ImageField' or similar
image.img.save("filename.png", image_file_like)
image.save()
print("works")
return render(request, 'page.html')
P.S:
You don't need to add 'media' prefix in your image file name while saving it.
You should add MEDIA_ROOT variable in your settings.py like this:
BASE_DIR = Path(__file__).resolve().parent.parent # or manually set the path of your project
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
And all files automatically will be saved in your project media directory.
I have processed the data retrieved from some API in a flask route, extracted the json needed and then stored it in a dictionary. Now, how do I make the processed data appear in a new HTML? Is it a good way to preprocess the data in a flask function, what if the preprocess takes a lot of steps?
Flask python code x.py:
#app.route('/test', methods = ['GET'])
def get_from_api():
url = "https://xxxx"
API_KEY = "xxxx"
params = {
'token' : API_KEY
}
response = requests.get(url, params).json()
city = response['city']
state = response['region']
postal = response['postal']
conutry = response['country']
location = response['loc']
data = {
"city" : city,
"state" : state,
"postal" : postal,
"country" : conutry,
"location" : location
}
Next is the x.html for displaying the data
<div class = "element_val">
<li><div id = "city">null</div></li>
<li><div id = "state">null</div></li>
...
<li><div id = "location">null</div></li>
</div>
How to retrieve the data in flask and put the data in html, and make the html be rendered in '/test'?
#app.route('/test')
def test_route():
user_details = {
'name': 'John',
'email': 'john#doe.com'
}
return render_template('test.html', user=user_details)
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<!-- use {{}} to access the render_template vars-->
<p>{{user.name}}</p>
<p>{{user.email}}</p>
</body>
</html>
I'm writing flask api using keras. However I get a lot of errors. One of them is Error 405 - method not allowed.
POST http://0.0.0.0:5000/static/predict 405 (METHOD NOT ALLOWED) jquery-3.3.1.min.js
I'm trying to get predictions written on the page, but they didn't show even before that error 405.
I don't know which place can lead to that error.
Here is code:
predict.html
<body>
<input id="image-selector" type="file">
<button id="predict-button"> Predict</button>
<p style="font-weight:bold">Predictions</p>
<p> Jablko <span id="apple-prediction"></span></p>
<p> Banan <span id="banana-prediction"></span></p>
<img id="selected-image" src=""/>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
let base64Image;
$("#image-selector").change(function(){
let reader = new FileReader();
reader.onload = function(e){
let dataURL = reader.result;
$('#selected-image').attr("src", dataURL);
base64Image = dataURL.replace("data:image/jpg;base64,", "");
//console.log(base64Image);
}
reader.readAsDataURL($("#image-selector")[0].files[0]);
$("#apple-prediction").text("");
$("#banana-prediction").text("");
});
$("#predict-button").click(function(event){
let message = {
image:base64Image
}
//console.log(message);
$.post("http://0.0.0.0:5000/static/predict", function(response){
$("#apple-prediction").text(response.prediction.apple.toFixed(6));
$("#banana-prediction").text(response.prediction.banana.toFixed(6));
console.log(response);
});
});
</script>
</body>
predict.py
app = Flask(__name__)
def get_model():
global model
model=load_model('fc_model1.h5')
#model.load_weights('model_grocery.h5')
#graph = tf.get_default_graph
print("** Model loaded!")
def preprocess_image(image, target_size):
image = image.resize(target_size)
image = image.img_to_array(image)
image = np.expand_dims(image, axis=0)
return image
print("**Loading model**")
get_model()
#app.route("/predict", methods=["POST"])
def predict():
message = request.get_json(force=True)
encoded = message['image']
decoded = base64.b64decode(encoded)
image = Image.open(io.BytesIO(decoded))
processed_image = preprocess_image(image, target_size=(224, 224))
#bt_prediction = vgg16.predict(processed_image)
prediction = model.predict(processed_image).tolist()
response = {
'prediction': {
'apple': prediction[0][0],
'banana': prediction[0][1]
}
}
return jsonify(response)
The error shows in google-chrome.
Your JS code has
$.post("http://0.0.0.0:5000/static/predict")
but your Flask route is
#app.route("/predict", methods=["POST"])
def predict():
Because the snippet you posted doesn't show that you're prepending /static/ to all routes, it looks like that's a mistake.
You specified methods=['POST'] correctly, so visiting 127.0.0.1:5000/predict should yield the expected result.
If you wanted to check 0.0.0.0:5000/predict, you need to add app.run(host='0.0.0.0')(See: Configure Flask dev server to be visible across the network).
I can make the simple button or default button work but as there is no option to custom css I need to use "Custom" stripe button. Following is the simple and custom form I am using where simple one works fine but don't know how to make custom work. Currently it is doing nothing when I click and enter information. But Default button works just fine.
View:
#app.route('/monthly', methods=['GET', 'POST'])
def monthly_charged():
if not user_authorized():
return redirect('/')
amount = 1495
# customer
key = stripe_keys['publishable_key']
print key
charge_all = stripe.Charge.list(limit=10000)
charge_dic = {}
charge_list = []
for charge_data in charge_all:
charge_dic['Amount'] = "$" + str(float(charge_data.amount) / 100) + " " + charge_data.currency.upper()
charge_dic['Description'] = charge_data.description
charge_dic['Name'] = charge_data.receipt_email
charge_dic['Date'] = str(datetime.datetime.fromtimestamp(charge_data.created))
charge_list.append(charge_dic)
charge_dic = {}
data = get_profile_data(session['auth_token'])
profile_data = data['StudentProfile']
student_id = profile_data.id
student = get_profile_data(session['auth_token'])['StudentProfile']
pkg = Package.query.filter_by(student_id=profile_data.id).first()
if pkg:
flash('You already have an active subscription.')
else:
stripe_token = request.form['stripeToken']
email = request.form['stripeEmail']
try:
customer = stripe.Customer.create(
email=email,
source=request.form['stripeToken']
)
subscription = stripe.Subscription.create(
customer=customer.id,
plan="monthly",
)
student_id = profile_data.id
student.stripe_customer_id = customer.id
student.stripe_subscription_id = subscription.id
package = Package(
student_id=student_id,
stripe_id = customer.id,
student_email=request.form['stripeEmail'],
is_active=True,
package_type='monthly',
subscription_id=subscription.id
)
dbase.session.add(package)
flash("You've successfylly subscribed for monthly package.")
dbase.session.commit()
except stripe.error.CardError as e:
# The card has been declined
body = e.json_body
err = body['error']
return redirect(url_for('packages', key=key, amount=amount))
Simple or Default Stripe Button:
<form action="/monthly" method="post" >
<div class="form-group">
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_YgHVTCLIMQLW4NV6ntnJPAXs"
data-description="Monthly Package"
data-name="Monthly"
data-amount="10000"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="auto">
</script>
</div>
</form>
Custom Stripe Button:
<form action="/monthlycharged" method="post">
<script src="https://checkout.stripe.com/checkout.js"></script>
<button id="customButton">Enroll</button>
<style>
#customButton{
width:100px;
height:30px;
background-color:red;
color:white;
border:2px solid red;
}
</style>
<script>
var handler = StripeCheckout.configure({
key: 'pk_test_YgHVTCLIMQLW4NV6ntnJPAXs',
image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
locale: 'auto',
token: function(token) {
// You can access the token ID with `token.id`.
// Get the token ID to your server-side code for use.
}
});
document.getElementById('customButton').addEventListener('click', function(e) {
// Open Checkout with further options:
handler.open({
name: 'Monthly',
description: 'monthly',
amount: 10000
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
</form>
You need to submit your form in the token: function() {} of StripeCheckout.configure.
Here's an example of how to do that: https://jsfiddle.net/osrLsc8m/
I created an API that can get data from MySQL. It working if the stockList is defined. However, in actual world, I need to get it from the Ionic app, and the stockList is defined by individual user.
Simply put stockList =[] does not work.
Currently the flask_app.py is as below:
from flask import Flask,jsonify,abort,make_response,request
import MySQLdb
import MySQLdb.cursors
app = Flask(__name__)
#app.route('/KLSEwatch', methods=['GET'])
def KLSEwatch():
db = MySQLdb.connect(host='vinvin.mysql.pythonanywhere-services.com',user='vinvin',passwd='xxx',db='vinukdb$default',cursorclass=MySQLdb.cursors.DictCursor)
curs = db.cursor()
stockList = ['SHELL','GENM']
placeholders = ','.join(['%s'] * len(stockList))
query = 'SELECT * FROM KLSE WHERE Stock IN ({})'.format(placeholders)
curs.execute(query,tuple(stockList))
f = curs.fetchall()
return jsonify({'Stock': f})
what I shall replace stockList as it shall get the data from user, which is from an Ionic app. The data is can be string or a 4 digits numbers
Below is the code in watchlistCtrl.js in Ionic app
//setting get counter-number of get requests-
var getCounter = 0;
for (var market in watchListQuery) {
if(watchListQuery[market].length>0){
getCounter += 1;
}
}
if(getCounter == 0)
$ionicLoading.hide();
$scope.watchedStocks = [];
for (var market in watchListQuery) {
if(watchListQuery[market].length>0){
var queryString = watchListQuery[market].toString().replace(/,/g, "','");
$webServicesFactory.get($marketProvider[market].queryURL+"/watchlist_query", {AnonymousToken: $marketProvider[market].token}, {parameters:{stockList:queryString}}).then(
function success(data) {
getCounter -=1 ;
$scope.watchedStocks = $scope.watchedStocks.concat(data);
if(getCounter <= 0)
$ionicLoading.hide();
},
function error() {
$ionicLoading.hide();
}
);
}
}//end of for each loop
You didn't show us any of your Ionic code, but here's a simple example of taking input from your Ionic app and submitting it to Flask. First, some HTML for the frontend (I'm only using Angular, since that is the common theme here - the rest of Ionic isn't relevant to this problem):
<!-- templates/home.html -->
<!doctype html>
<html lang="en">
<head>
<title>Ionic / Flask</title>
</head>
<body>
<div ng-app="app">
<div ng-controller="MyCtrl">
<p>Enter a comma-separated string value, like "BAC,XYZ"</p>
<input type="text" ng-model="stockList">
<button ng-click="submit()">Submit</button>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.14/angular.js"></script>
<script>
angular.module('app', [])
.controller('MyCtrl', function ($http, $log, $scope) {
$scope.stockList = '';
$scope.submit = function () {
$http.get('/KLSEwatch', {params: {stockList: $scope.stockList}}).then(function (result) {
$log.log('This is the query to execute: ',result.data)
})
};
})
</script>
</body>
</html>
And then here's a modified version of your Flask app, to demonstrate that this will generate the correct query:
# app.py
from flask import Flask,jsonify,abort,make_response,request, render_template
import MySQLdb
import MySQLdb.cursors
app = Flask(__name__)
app.config['DEBUG'] = True
#app.route('/')
def home():
return render_template('home.html')
#app.route('/KLSEwatch', methods=['GET'])
def KLSEwatch():
stockList = request.args['stockList'].split(',')
placeholders = ','.join(['%s'] * len(stockList))
query = 'SELECT * FROM KLSE WHERE Stock IN ({})'.format(placeholders)
print('This is the query: %s' % (query % tuple(stockList)))
return query % tuple(stockList)
app.run()
All you need to do is run the app, enter a string value into the input field & submit it, and then check the results in your browser console log, or in the output for the Flask app.