Python Flask and AJAX - Update progress bar during python method [duplicate] - python

I'm trying to implement server push in my Flask project following this tutorial.
I've set it all up with no errors, however when I go to the /stream page, Firefox recognizes it as a file and tries to download it. In Safari it just prints out the data sent. I tried adapting the code to a simpler implementation, where a thread just yields some data each second, however it produced the same results.
My goal is for each time a python script reaches a point in a loop, it will update a progress bar on the web interface.
Any help with this would be great. Thanks.
Edit:
app.py
from flask import Flask, render_template, request, Response
app = Flask(__name__)
def event_stream():
event = "Hello!"
yield 'data: %s\n\n' % event
#app.route('/stream')
def stream():
return Response(event_stream(), mimetype="text/event-stream")
if __name__ == "__main__":
app.debug = True
app.run(threaded=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
var source = new EventSource('/stream');
source.onmessage = function (event) {
alert(event.data);
};
</script>
</head>
<body>
<p>Stream page</p>
</body>
</html>

EDIT
I've uploaded my sample application to my Github. Check it out here: https://github.com/djdmorrison/flask-progress-example
I've worked it out, but for anyone else who gets the same problem:
The index.html page never actually loads, as it's never called in app.py. The way to do it is by going to a separate route, /page for example, and then returning send_file('index/html'). This will load the index page, create the EventSource linked to /stream, which will then start the stream method in app.py and yield the correct data.
Example which creates a progress bar by increasing x every 0.2 seconds and displaying it on the webpage:
app.py
#app.route('/page')
def get_page():
return send_file('templates/progress.html')
#app.route('/progress')
def progress():
def generate():
x = 0
while x < 100:
print x
x = x + 10
time.sleep(0.2)
yield "data:" + str(x) + "\n\n"
return Response(generate(), mimetype= 'text/event-stream')
progress.html
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script>
var source = new EventSource("/progress");
source.onmessage = function(event) {
$('.progress-bar').css('width', event.data+'%').attr('aria-valuenow', event.data);
}
</script>
</head>
<body>
<div class="progress" style="width: 50%; margin: 50px;">
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
</div>
</body>
</html>

Related

404 error running python script from html button | Google App Engine website hosted

I have an html page, custom.html, and a python script, test.py (both screenshotted below). The html page only has a button that should trigger the python script to print a line (in my next python script I'd like it to do more but this is my starting point).
Under Chrome's developer tools, once I click the button, I receive a GET 404 error, initiated by Jquery. Any advice on successfully activating the python script from my html button is greatly appreciated.
My test.py script is simply
print("Successful line print")
Here is my custom.html document
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Page Title</title>
<link rel="stylesheet" type="text/css" href="../static/css/style2.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<pre>
</pre>
<body>
<div>
<div>
<button style="text-align: center; margin-bottom: 150px" class="search-btn" type="button" value=" Run Script " onclick="goPython()">Trigger Python
<script>
function goPython() {
$.ajax({
url: "../folder/test.py",
context: document.body
}).done(function() {
alert('finished python script');;
});
}
</script>
</button>
</div>
</div>
</body>
</html>
EDIT: I am adding the code to my main.py script required for Google App Engine to handle URL calls and importing Flask.
from flask import Flask, request, render_template
app = Flask(__name__)
#app.route("/")
def index():
return render_template('index.html')
#app.route("/learn.html")
def learn():
return render_template('learn.html')
#app.route("/custom.html")
def custom():
return render_template('custom.html')
if __name__ == "__main__":
app.run()
EDIT 2, after attempting #Dustin Ingram's answer:
Here is the new code to my custom.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Page Title</title>
<link rel="stylesheet" type="text/css" href="../static/css/style2.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<div>
<div>
<button style="text-align: center; margin-bottom: 150px" class="search-btn" type="button" value=" Run Script " onclick="goPython()">Click Here
<script>
function goPython() {
$.ajax({
url: "/trigger-python",
context: document.body
}).done(function() {
alert('finished python script');;
});
}
</script>
</button>
</div>
</div>
</body>
</html>
And I made a simple test.py just to test the ability for the html button click to activate the python script
from flask import Flask
app = Flask(__name__)
#app.route("/trigger-python")
def print_something():
print("Successful line print")
return 'OK'
print_something()
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)
After fixing the URL called in AJAX, I'm still getting the same 404 error when clicking the button. Below is an updated Chrome Developer Tool screenshot.
You can't call a Python script via an AJAX request like that. You'll need to call a URL that corresponds to an endpoint of a Python web application.
So, for example, on the frontend:
$.ajax({
url: "/do-something",
context: document.body
})
and then on the backend there is a corresponding route:
#app.route("/do-something")
def do_something():
print("Successful line print")
return 'OK'
See https://cloud.google.com/appengine/docs/standard/python3/quickstart for details on getting started with Python web applications on App Engine.
EDIT: Here's exactly what I've tested and confirmed works:
app.yaml:
runtime: python37
requirements.txt:
Flask==1.1.2
main.py:
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/custom.html')
def custom():
return render_template('custom.html')
#app.route("/trigger-python")
def print_something():
print("Successful line print")
return 'OK'
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)
templates/custom.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Page Title</title>
<link rel="stylesheet" type="text/css" href="../static/css/style2.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<div>
<div>
<button style="text-align: center; margin-bottom: 150px" class="search-btn" type="button" value=" Run Script " onclick="goPython()">Click Here
<script>
function goPython() {
$.ajax({
url: "/trigger-python",
context: document.body
}).done(function() {
alert('finished python script');;
});
}
</script>
</button>
</div>
</div>
</body>
</html>
You can see it working here: https://stackoverflow-61195723.uc.r.appspot.com/custom.html

Pass slider value to bottle route function

I am trying to run a code but one part is not working, where I need to pass the value from a slider. I can see the value in html page but I can not pass it to inputRange() where I need to run some commands.
I've tried to isolate only the code which work with slider. Could you please let me know how can I pass the slider value to val_slide? Thanks.
Code:
from bottle import route, run, template
IP_ADDRESS = '192.168.0.80'
PORT = 8080
#route('/')
def hello():
return '<b>Test</b>'
#route('/remote')
def remote():
return '''<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css">
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script>
<script>
$(document).ready(function() {
$('#inputRange').on('change mouseup', function(){
var val_slide ='inputRange';
$.ajax({
url: '/remote/inputRange',
type: 'GET',
data: {command:val_slide},
});
});
});
</script>
<style></style>
</head>
<body>
<div data-role="page">
<div data-role="main" class="ui-content">
<form>
<div style="text-align:center">
<label for="switch">Test</label>
<div class="slidecontainer">
<input id="inputRange" type="range" min="0" max="100" step="1" value="20" class="slider" name='data'>
<output name="inputRange"/output>
</div>
</form>
</div>
</div>
</body>
</html>'''
#route('/remote/inputRange')
def inputRange():
print val_slide
# use val_slide value
return 'inputRange'
try:
run(host = IP_ADDRESS, port= PORT)
except(KeyboardInterrupt):
print('Done!')
quit()
To access query parameters from GET request you should import request and use request.query to access to values by name:
from bottle import route, request
#route('/remote/inputRange')
def inputRange():
val_slide = request.query.command
print(val_slide)
return val_slide
I'm not big specialist in JavaScript, but as far as I know, to send actual value (not just static text) you need to replace val_slide static text assignment with reading of value:
var val_slide = this.value;

How to create html using user input and display it in a new tab?

I would like to run a flask application where the user can provide some user input which is used to create a HTML page which should then be displayed in a new tab. The HTML is created using an external tool (here mimicked by the function get_html which actually takes the user input as argument), so I cannot just use a template which I render (I think).
I can already take the user input and create the HTML I would like to see displayed, however, I did not manage to also open a new tab for it. How can this be achieved?
Here is my code:
from __future__ import print_function, division
from flask import Flask, render_template, request, jsonify
import json
# Initialize the Flask application
app = Flask(__name__)
#app.route('/html_in_tab')
def get_html():
# provided by an external tool
# takes the user input as argument (below mimicked by a simple string concatenation)
return '<!DOCTYPE html><title>External html</title><div>Externally created</div>'
#app.route('/_process_data')
def data_collection_and_processing():
# here we collect some data and then create the html that should be displayed in the new tab
some_data = json.loads(request.args.get('some_data'))
# just to see whether data is retrieved
print(some_data)
# oversimplified version of what actually happens; get_html comes from an external tool
my_new_html = get_html() + '<br>' + some_data
print(my_new_html)
# this html should now be displyed in a new tab
return my_new_html
#app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
The index.html looks as follows:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="header">
<h3 class="text-muted">Get new tab!</h3>
</div>
<button type="button" id="process_input">Process!</button>
<a href="/html_in_tab" class="button" target='_blank'>Go to results</a>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
// clicking the button works fine: data is processed correctly
$('#process_input').bind('click', function() {
$.getJSON('/_process_data', {
some_data: JSON.stringify('some data')
});
// can this be changed to show the processed html?
window.open("/html_in_tab", "_blank");
return false;
});
});
</script>
</body>
</html>
So, now the window.open part opens a new tab, but it should display my_new_html, the newly created HTML by data_collection_and_processing. How can I achieve that?
At the moment you're just opening a new window at the endpoint "/html_in_tab" which will hit the Flask route for get_html() and show the standard HTML with no user input.
One method you could try is to open a new window and set the document body innerHTML with the updated content:
<script type="text/javascript">
$(document).ready(function() {
$('#process_input').bind('click', function() {
$.get('/_process_data', {
some_data: JSON.stringify('some data'),
}).success(function(data) {
var win = window.open("", "_blank");
win.document.body.innerHTML = data;
})
return false;
});
});
</script>
Change your html as shown below:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="header">
<h3 class="text-muted">Get new tab!</h3>
</div>
<button type="button" id="process_input">Process!</button>
<a href="/html_in_tab" class="button" target='_blank'>Go to results</a>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
// clicking the button works fine: data is processed correctly
$('#process_input').bind('click', function() {
$.getJSON('/_process_data', {
some_data: JSON.stringify('some data')
});
// can this be changed to show the processed html?
window.open("/process_data", "_blank");
return false;
});
});
</script>
</body>
</html>
and Python script as shown below:
from __future__ import print_function, division
from flask import Flask, render_template, request, jsonify
import json
# Initialize the Flask application
app = Flask(__name__)
#app.route('/html_in_tab')
def get_html():
# provided by an external tool
return '<!DOCTYPE html><title>External html</title><div>Externally created</div>'
#app.route('/_process_data')
def data_collection_and_processing():
# here we collect some data and then create the html that should be displayed in the new tab
some_data = json.loads(request.args.get('some_data'))
# just to see whether data is retrieved
print(some_data)
# oversimplified version of what actually happens; get_html comes from an external tool
my_new_html = get_html() + '<br>' + some_data
with open('templates/changed_html.html','w') as f: #write the html string to file
f.writelines(my_new_html)
# this html should now be displyed in a new tab
return ''
#app.route('/process_data')
def process_data():
return render_template('changed_html.html')
#app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)

How to create a progress bar using flask? [duplicate]

This question already has answers here:
Flask App: Update progress bar while function runs
(3 answers)
Closed 1 year ago.
Just want to insert a progress bar in my html page. It should load from a for in my app.py. That's what I did so far...
app.py
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/progress')
def ajax_index():
for i in range(500):
print("%d" % i)
# I want to load this in a progress bar
if __name__ == "__main__":
app.run(debug=True)
I'm using a bootstrap progress-bar from w3schools in my code
index.html
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(function () {
$("#content").load("/progress");
});
</script>
</head>
<body>
<div class="container">
<h2>Progress Bar With Label</h2>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" style="width:0%"></div>
</div>
</div>
</body>
</html>
Any help, please?
this is pretty simple: poll your api and update the progress bar width and valuenow until finished:
var interval = setInterval(update_progress, 1000);
function update_progress() {
$.get('/progress').done(function(n){
n = n / 5; // percent value
if (n == 100) {
clearInterval(interval);
callback(); // user defined
}
$('.progress-bar').animate({'width': n +'%'}).attr('aria-valuenow', n);
}).fail(function() {
clearInterval(interval);
displayerror(); // user defined
});
}

text/event-stream recognised as a download

I'm trying to implement server push in my Flask project following this tutorial.
I've set it all up with no errors, however when I go to the /stream page, Firefox recognizes it as a file and tries to download it. In Safari it just prints out the data sent. I tried adapting the code to a simpler implementation, where a thread just yields some data each second, however it produced the same results.
My goal is for each time a python script reaches a point in a loop, it will update a progress bar on the web interface.
Any help with this would be great. Thanks.
Edit:
app.py
from flask import Flask, render_template, request, Response
app = Flask(__name__)
def event_stream():
event = "Hello!"
yield 'data: %s\n\n' % event
#app.route('/stream')
def stream():
return Response(event_stream(), mimetype="text/event-stream")
if __name__ == "__main__":
app.debug = True
app.run(threaded=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
var source = new EventSource('/stream');
source.onmessage = function (event) {
alert(event.data);
};
</script>
</head>
<body>
<p>Stream page</p>
</body>
</html>
EDIT
I've uploaded my sample application to my Github. Check it out here: https://github.com/djdmorrison/flask-progress-example
I've worked it out, but for anyone else who gets the same problem:
The index.html page never actually loads, as it's never called in app.py. The way to do it is by going to a separate route, /page for example, and then returning send_file('index/html'). This will load the index page, create the EventSource linked to /stream, which will then start the stream method in app.py and yield the correct data.
Example which creates a progress bar by increasing x every 0.2 seconds and displaying it on the webpage:
app.py
#app.route('/page')
def get_page():
return send_file('templates/progress.html')
#app.route('/progress')
def progress():
def generate():
x = 0
while x < 100:
print x
x = x + 10
time.sleep(0.2)
yield "data:" + str(x) + "\n\n"
return Response(generate(), mimetype= 'text/event-stream')
progress.html
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script>
var source = new EventSource("/progress");
source.onmessage = function(event) {
$('.progress-bar').css('width', event.data+'%').attr('aria-valuenow', event.data);
}
</script>
</head>
<body>
<div class="progress" style="width: 50%; margin: 50px;">
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
</div>
</body>
</html>

Categories

Resources