I am creating the web app, which will process data, so I am using Server Sent Event in Flask.
I need to store session variable in a SSE function, but flask doesn't see it outside the function. Can I fix it somehow?
MWE:
server.py:
from flask import Flask, render_template, session, Response, stream_with_context
import time
app = Flask(__name__)
app.secret_key = b'132d2dcf59f9604c0b48e4e3a1a1cd19a0abf121b48a4777'
#app.route('/')
def get_page():
return render_template('progress.html')
#app.route('/progress')
def progress():
def generate():
x = 0
while x < 100:
x = x + 10
print(x)
time.sleep(0.2)
yield "data:" + str(x) + "\n\n"
session['result'] = x
print(session['result']) #100
return Response(stream_with_context(generate()), mimetype= 'text/event-stream')
#app.route('/done')
def done():
print(session['result']) #KeyError: 'result'
return session['result']
if __name__ == '__main__':
app.run(debug=True)
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);
if (event.data >=100) {
source.close();
window.location = "http://127.0.0.1:5000/done"
}
}
</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>
The session is a cookie. Cookies are sent as headers. Headers are sent first in a response, before the rest of the stream is sent. You can't modify the session after beginning the response. Send whatever data you need to in the stream, and handle it on the receiving end.
Related
I am making a flask chat application using flask-socketio. When I click the send button no messages are displayed on the screen. I have referenced the flask-socketio documentation for setting everything up. Can someone please help me here.
mainapp.py snippet
from flask_socketio import SocketIO, send, emit
app = Flask(__name__)
app.secret_key = 'replace later'
# Initialise Flask-Socketio
socketio = SocketIO(app)
#app.route("/chat", methods=['GET', 'POST'])
def chat():
return render_template('chat.html')
#socketio.on('message')
def message(data):
#print(f"\n{data}\n")
send(data)
chat.html snippet
<div id="display-message-section">
</div>
<!-- Input area -->
<div id="input-area">
<input type="text" id="user_message" placeholder="Type here..." autocomplete="off">
<button type="button" id="send_message">SEND</button>
</div>
<!-- SocketIO JS -->
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I="
crossorigin="anonymous"></script>
<!-- Custom SocketIO JS -->
<script src="{{ url_for('static', filename='scripts/socketio.js') }}"></script>
socketio.js (which is in static/scripts) snippet
document.addEventListener('DOMContentLoaded', () => {
var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('connect', () => {
socket.send("I am connected");
});
socket.on('message', data =>{
const p = document.createElement('p');
const br = document.createElement('br');
p.innerHTML = data;
document.querySelector('#display-message-section').append(p);
});
document.querySelector('#send_message').onclick = ()=>{
socket.send(document.querySelector('#user_message').value);
}
})
What the output looks like - Link to image 1
What is should look like - Link to image 2
Tried clearing the cookies and cache and everything is working fine right now.
I'm making a web app that checks to see if a user exist at various forums, then returns the results dynamically on web page via AJAX.
I'm using flask, which calls on the original python script.
main.py:
app = Flask(__name__)
#app.route('/')
def index():
return render_template("index.html")
#app.route('/', methods=['POST'])
def search_form():
x = request.form['searchinput']
a = Vbulletin(x)
def result_gen():
return a.reg_ver()
result_gen()
for s in result_gen():
text = s
return render_template("index.html", text=text)
if __name__ == "__main__":
app.run(debug=True)
the variable text is what i would like to dynamically display in Html as each response comes in. The way my code is written now it only displays the last item.
AJAX:
$(document).ready(function() {
$('form').on('submit', function(event) {
$.ajax({
data : {
x : $('#searchInput').val(),
},
type : 'POST',
url : '/'
})
event.preventDefault();
});
});
index.html:
<!DOCTYPE html>
<html>
<head>
<title>UserFind Home</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='main.css') }}">
<script type="text/javascript" src="{{url_for('static', filename='forumsearch.js')}}"></script>
</head>
<body>
<nav>
<ul id="navlist">
<h1>Userfind</h1>
<li><a class="btn" href="#home">Home</a></li>
<li><a class="btn" href="#contact">Contact</a></li>
<li><a class="btn" href="#about">About</a></li>
<form method="POST" name=searchbar>
<ul id="navsearch">
<li class="search">
<input type="text" id="searchinput" name="searchinput" placeholder="Search for User here. Must be atleast 5 characters long.">
</li>
<li><button type="submit" class="btn-default">Submit</button></li>
</ul>
</form>
</ul>
</nav>
<script>
var btnContainer = document.getElementById("navlist");
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function() {
var current = document.getElementsByClassName("active");
if (current.length > 0) {
current[0].className = current[0].className.replace(" active", "");
}
this.className += " active";
});
}
</script>
<p>{{ text }}</p>
<div class="footer">
<p>©2019 Userfind</p>
</div>
</body>
</html>
I've found a lot of information on how to pass a single value( or multiple values one time), but how can I pass each value from flask to html dynamically as a response is received?
Edit:
This is the code that gets called with a.reg_ver():
def reg_ver(self):
urlfile = json.load(open("/home/vbulletin_regversion_SHORT_TESTING_dict.txt"))
for url, url_veri in urlfile.items():
try:
payload = {'username': self.name, 'securitytoken': 'guest', 'do': 'verifyusername'}
headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'}
s = requests.Session()
s.get(url, headers=headers)
response = s.post(url_veri, headers=headers, data=payload)
soup = BeautifulSoup(response.text, "html.parser")
message = soup.find('message').text
if self.name in message:
result = (url, "user found!!! I SAID USER FOUND!!!")
yield result
elif message == "Username is valid and not in use.":
result = (url, "user not found")
yield result
else:
result = (message, "possible error")
yield result
The code checks for a user from a forum in the list, then yields the result. I pass result to flask with this:
def result_gen():
return a.reg_ver()
result_gen()
for s in result_gen():
text = s
Can I append to a temporary list, and dynamically add each result to html, or am I going about this the wrong way?
Flask should send only text (or JSON) on AJAX requests and AJAX should get text (without HTML) and append() to HTML which is already in browser. So this way it can appends new line to existing text.
Working example:
from flask import Flask, render_template_string, request
app = Flask(__name__)
#app.route('/')
def index():
return render_template_string('''
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<form method="POST">
<input type="text" id="searchinput" name="searchinput">
<button type="submit">Submit</button>
</form>
<p></p>
<script>
$(document).ready(function() {
$('form').on('submit', function(event) {
$.ajax({
data: {x: $('#searchinput').val()},
type: 'POST',
url: '/',
}).done(function(data){
$('p').append(data+"<br>");
});
event.preventDefault();
});
});
</script>
</body>
</html>''')
import datetime
#app.route('/', methods=['POST'])
def search_form():
print(request.form)
data = request.form.get('x', '')
text = str(datetime.datetime.now()) + " | " + data
return text
if __name__ == "__main__":
app.run(debug=True)
Flask could also check if it is really AJAX requests and send full HTML when it is not AJAX. But to keep all lines of text it would have to remeber them somehow/somewhere - ie. in file, database or cookies.
BTW: you had few mistakes:
$('#searchinput') should have lower i but you have I.
AJAX sends data as x - see data: {x: $('#searchinput').val()}, but flask tries to get it as searchinput.
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded',()=>{
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect',()=>{
console.log('connected');
document.querySelector('#submit').onclick =() =>{
const user= document.querySelector('#user').value;
const room = document.querySelector('#room').value;
socket.emit('join',{'user':user,'room':room});
console.log('emitted');
return false;
};
});
});
</script>
</head>
<body>
<form id="new-task" action="{{ url_for('chat') }}" method="post">
<input type="text" autocomplete="off" autofocus id="user" placeholder="username">
<input type="text" autocomplete="off" autofocus id="room" placeholder="room">
<input type="submit" id="submit" value="join">
</form>
</body>
</html>
localhost:5000 open index.html page , when i click on submit only socketio is working but url is not being changed .
import os
import requests
from flask import Flask,jsonify,render_template,request
from flask_socketio import SocketIO,emit,join_room,leave_room,send
from werkzeug import secure_filename
app = Flask(__name__)
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")
socketio=SocketIO(app)
#app.route("/")
def index():
return render_template("index.html")
#app.route("/chat" ,methods=["POST"])
def chat():
print("running chat")
return render_template("chat.html")
#socketio.on('join')
def on_join(data):
username = data['user']
room = data['room']
join_room(room)
emit('chat',{'username':username},room=room)
print("room has been allocated")
when i did only url_for without socketio i was able to change route but now it does not seem to work. in console and cmd i am able to see socketio working but chat route is not working
OK so I am still struggling on this one, here is my complete code after using #DrAgon suggestion. I have 3 files, the first one is my Python file that merely get the local time.
import datetime
import time
now = datetime.datetime.now()
print(now)
If I set this file up as:
import datetime
import time
while True:
now = datetime.datetime.now()
print(now)
time.sleep(2)
It refreshes and gets me the date every 2 seconds but when it's imported in to my flask project it takes over and just will not load the web page.
so I use the first version of the file that gets the local time. Next I have my flask Python page:
from flask import Flask, render_template, request, jsonify
from flask_bootstrap import Bootstrap
import Timereppper
import datetime
now = datetime.datetime.now()
app = Flask(__name__)
Bootstrap(app)
#app.route('/')
def hello():
return render_template('hello.html', myvar= now.second)
#app.route('/currentstatus')
def currentstatus():
return render_template('hello.html', myvar= now.second)
#app.route('/historic')
def historic():
return render_template('historic.html')
#app.route('/data')
def get_data():
mbdata = Timereppper.now() # Call your code that gets the data here
return jsonify(mbdata) # And return it as JSON
if __name__ == '__main__':
app.run(host= 'localhost', port=5000, debug=False, threaded=True)
and then i have my main hello.html file with the code suggested by #DrAgon at the bottom. This returns the text on the bottom of the main webpage "New Data Goes Here" and if I look at lcalhost:5000/data I get the date that was read at the when the flask server was first started. I want this date to update continuously or every few seconds and display on the main home page of the website. Can anyne show me where I am going wrong. I apologise I am new to flask.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title> Vibration Monitor</title>
<meta name="viewport" content="width=device-wi dth, initial-scale=1">
<link href="{{url_for('static', filename='css/bootstrap.min.css')}}"
rel="stylesheet">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico')
}}">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<script>window.jQuery || document.write('<script src="{{
url_for('static', filename='jquery.js') }}">\x3C/script>')</script>
</head>
<nav class="navbar navbar-expand-lg navbar-light bg-#808080">
<a class="navbar-brand" href= https://www.iotindustries.co.uk>
<img src="/static/img/IOT Industries Logo 1THIN.png" width="300" height="90"
class="d-inline-block" alt="">
</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link font-weight-bold" href="/currentstatus">Current
Status</a>
</li>
<li class="nav-item active">
<a class="nav-link font-weight-bold" href="/historic">Historic</a>
</li>
</ul>
</nav>
<div class="alert alert-secondary" role="alert"></div>
<div class="card-deck" style="width: 42rem;">
<div class="card text-white bg-dark mb-3" style="width: 16rem;">
<img class="card-img-top" src="/static/img/vibes1.png" alt="Vibration
Image">
<div class="card-body">
<h5 class="card-title">Current vibration level:</h5>
<h1 class="card-text font-weight-bold">15 mA</h1>
<a class="btn btn-success">Acknowledge</a>
</div>
</div>
<div class="card text-white bg-dark mb-3" style="width: 16rem;">
<img class="card-img-top" src="/static/img/timer.svg" alt="Timer Image">
<div class="card-body">
<h5 class="card-title">Estimated days until failure:</h5>
<h1 class="card-text font-weight-bold"> 3 Days {{myvar}} </h1>
<a class="btn btn-info" href="/historic">View Historic</a>
</div>
</div>
</div>
<body>
<div id="myDataDiv">New Data Goes Here</div>
</body>
<script>
var goGetNewData = function() {
$.ajax({
url: '/data',
dataType: 'json',
type: 'get',
success: function(e) {
$('#myDataDiv').html('New Data ' + e);
},
error: function(e) {
$('#myDataDiv').html('Error');
},
});
}
var waitTime = 10; // 10 seconds
var fetchInterval = window.setInterval(goGetNewData, waitTime*1000);
</script>
</html>
I have a Python file which is constantly reading a Modbus register every couple of seconds, using the Python sleep.() function. This value is getting logged to a google FireBase database.
What I would like to do is display this value read from the ModBus register on my Flask website. I just don't know how to do it. Am I able to either:
Get the updated ModBus value from the Python file, pass that in to "MyFlaskApp.py" continuously or at the click of a button in my HTML file.
Query the Firebase database and get this to display the latest written value either continuously or at the click of a button.
Which, if any of these, can be done and what would be the best method. Can anyone post an example of how I would be able to do this?
Thanks
Well If you want to do it on a click of a button, you can make the button part of an html form and have it post to your flask application:
#app.route('/update')
def val():
return render_template("index.html",data=data)
This would work, considering the value you want to pass to the html is called data.
To display the data passed through, your html should look like this:
<p>{{data}}</p>
Instead of updating the modbus value every two seconds, you could do it only when the button is clicked like so:
#app.route('/update')
def val():
#code to get the value
return render_template("index.html",data=data)
This way everytime you click the form button to get a new value, then the data is read from the database. Instead of using another file as an import and using datetime, this would make sure that your program not only saves memory but still returns the desired value.
In the now method, you should write to a txt file as such:
#get the data
f = open("mod.txt", w+)
f.write(data)
f.close
Then when you recive the button click in the flask app:
#app.route('/update')
def val():
f = open("mod.txt",r)
data = f.read()
f.close()
return render_template("index.html",data=data)
To make sure that the other program is running you can do this:
if __name__ == '__main__':
execfile('modbusreginfo.py')
app.run(host= 'localhost', port=5000, debug=False, threaded=True)
If you want to make the page reload every 2 seconds so there is no button click you can add this to your html:
<meta http-equiv="Refresh" content="2">
Here's one way.
You will need to create 2 routes in your MyFlaskApp.py
/home - This route will render an HTML template
/data - This route will return JSON (containing the data from your ModBus file OR from querying Firebase)
(you can name the routes whatever you want)
In the HTML template returned by the first route you will need a div (with an id) to render the data and some JavaScript to continuously fetch new data.
Then just point the Ajax GET at your /data endpoint and your on your way.
<body>
<div id="myDataDiv">New Data Goes Here</div>
</body>
<script>
var goGetNewData = function() {
$.ajax({
url: '/data',
dataType: 'json',
type: 'get',
success: function(e) {
$('#myDataDiv').html('New Data ' + e);
},
error: function(e) {
$('#myDataDiv').html('Error');
},
});
}
var waitTime = 10; // 10 seconds
var fetchInterval = window.setInterval(goGetNewData, waitTime*1000);
</script>
The /data route could look something like this.
from flask import jsonify
import Modbusreginfo # Import your other python code
#app.route('/data')
def get_data():
mbdata = Modbusreginfo.getModBusData() # Call your code that gets the data here
return jsonify(mbdata) # And return it as JSON
I want my flask app to send a file and then redirect to the home page.
def create_pdf(**kwargs):
page = PdfManager(**kwargs)
pdf_out = page.create_pdf()
response = make_response(pdf_out)
# redirect(url_for('home'))
response.headers['Content-Disposition'] = "attachment; filename=pdf-test.pdf"
response.mimetype = 'application/pdf'
return response
app.route('/', methods=['GET', 'POST'])
def home():
create_pdf(foo='bar')
This piece of code properly spits out the pdf file on response but I can't make the page to refresh or redirect after downloading the pdf file. I can't use send_from_directory method of flask since this pdf file is dynamically generated using StringIo , PdfFileWriter objects.
You can make the "/" path return a html response with javascript like:
<!DOCTYPE html>
<html lang="en">
<head>
<script>
setTimeout(function(){
document.location = "/redirect-uri";
}, 500)
</script>
</head>
<body>
<iframe width="0" height="0" src="/path-to-pdf.pdf"/>
</body>
</html>
If the pdf is dynamically generated, you should also serve the pdf url like
#route('/path-to-pdf.pdf')
def pdf_generator():
return create_pdf()
A slightly better way might be to let javascript handle submitting the form as well as the redirect:
<form name="createPdf">
<!-- Your fields etc. here -->
<a href="javascript:submitAndRedirect()">
<button>Submit</button> <!-- NB do not use type=submit -->
</a>
</form>
<script type="text/javascript">
function submitAndRedirect() {
document.createPdf.submit();
setTimeout(function() {
location.href = YOUR_REDIRECT_URL_HERE;
}, 500);
}
</script>
The redirectUrl could be injected from python if necessary like this:
return render_template("create_pdf.html", redirect_url=url_for("whatever"))
Then in your javascript:
var redirectUrl = '{{ redirect_url }}';