How do you create a stream inside a Flask webpage? [duplicate] - python

This question already has answers here:
Display data streamed from a Flask view as it updates
(3 answers)
Closed 2 years ago.
I'm running a bot and I'm trying to output the bot's logs to a simple webpage as a stream of HTML content (each log is sent as <p>{log}</p>). If I output the stream to route "/log_stream" and then go to that route, I can see the logs and they update and everything works as expected. However, I can't figure out how to incorporate that stream in another webpage. I tried embedding it using an <iframe> with the "/log_stream" route as the src but then the iframe wouldn't render at all. Any idea how to make this work?
NOTE: Please don't mark this as a duplicate of Display data streamed from a Flask view as it updates The answer there is either outdated or incorrect. Although you can't update the template directly after it's been rendered on the server, you definitely can embed dynamic content in that template using an iframe or img, like in this tutorial where he uses an image dynamically to create a video stream. Not being able to dynamically update the template itself is NOT the same thing as not being able to dynamically update the content IN that template.
app.py:
from flask import Flask, render_template
from threading import Thread
from bot import Bot
from logger import Logger
from config import config
application = Flask(__name__)
logger = Logger()
#application.route('/')
def index():
return render_template('index.html.jinja', products=config['targets'], status='running')
#application.route('/log_stream')
def log_stream():
return logger.printerStream()
if __name__ == '__main__':
bot = Bot(logger)
botThread = Thread(target=bot.run)
botThread.start()
application.run(host='0.0.0.0', debug=True)
logger.py (relevant parts):
def getLogHTML(self, logObj):
if logObj['type'] == INFO:
return "<p style=\"color:green\">"+logObj['text']+"</p>\n"
if logObj['type'] == WARN:
return "<p style=\"color:yellow\">"+logObj['text']+"</p>\n"
if logObj['type'] == ERROR:
return "<p style=\"color:red\">"+logObj['text']+"</p>\n"
if logObj['type'] == EXCITE:
return "<p style=\"color:blue\">"+logObj['text']+"</p>\n"
return "<p style=\"color:white\">"+logObj['text']+"</p>\n"
def printerStream(self):
self.isStreaming = True
self.logs = self.getStoredLogs()
self.logs.extend(self.secondaryLogs)
self.storeLogs(self.secondaryLogs)
self.secondaryLogs = deque()
def generate():
try:
while True:
if len(self.logsToStore) > 0:
self.storeLogs(self.logsToStore)
self.logsToStore = deque()
nextLog = self.getNextLog()
nextLogHTML = self.getLogHTML(nextLog)
yield nextLogHTML
except GeneratorExit:
self.isStreaming = False
self.isStreaming = False
return Response(generate(), mimetype='text/html') # maybe text/html?
index.html.jinja:
<!doctype html>
<head>
<title>Title</title>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
body {
color: green
}
.title {
display: flex;
flex-direction: row;
justify-content:center
}
h1 {
margin-bottom: 6px;
}
.row {
display: flex;
}
.column {
flex: 50%
}
</style>
</head>
<body>
<div class="title">
<h1>Title</h1>
<h2>Subtitle</h2>
</div>
<div class="row">
<div class="column">
</iframe src="{{ url_for('log_stream')}}">
</div>
<div class="column">
<div class="row">
<div class="column">
<p>Products: </p>
</div>
<div class="column">
{% for product in products %}
<p>{{product.name}}</p>
{% endfor %}
</div>
</div>
<div class="row">
<div class="column">
<p>Status: </p>
</div>
<div class="column">
<p>{{ status }}</p>
</div>
</div>
</div>
</div>
</body>

Actually, I solved this by adding styling to the <iframe>. Changing it to:
<iframe frameborder='0' noresize='noresize'
style='position: absolute; background: transparent; width: 100%; height:100%;'
src="{{ url_for('log_stream')}}"
frameborder="0"></iframe>
accomplished pretty much what I wanted!

Related

Why is my flask web app not working properly? I can't find any errors myself

So I tried to write my first flask web application and the website isn't working. Here is my application.py. I have already tried to get the input. Is a second web page in case of error necessary? Because the website itself isn't running in my IDE and I can't make out any errors.
from flask import Flask, render_template, request
import requests
import json
from tempfile import mkdtemp
from flask_session import Session
import random
app = Flask(__name__)
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
# import permutation module
from itertools import permutations as prm
scrambled_word = list(str(raw_input("Input your jumbled word: ")))
# empty lists that will be appended to later
prm_list = []
possible_words = []
# take each permutation of the input and put in a list
for i in prm(scrambled_word):
prm_list.append("".join(i))
print i
def check(x, y):
# open list of words
dictionary = file('words.txt')
# check each line in the dictionary against each item
# in the list of permutations and add it to another empty list if it's a match
for line in dictionary:
for i in x:
if i+'\n' == line:
y.append(i)
check(prm_list, possible_words)
# delete duplicates
possible_words = list(set(possible_words))
# print out possible words
if len(possible_words) == 0 or len(possible_words) == 1 or len(possible_words) == 2 or len(possible_words) == 3:
print "No match found"
else:
for i in possible_words:
print "Possible Word for Jumbled Word is: " + i
#app.route("/", methods=["GET", "POST"])
def index():
# make sure that method is POST
if request.method == "POST":
app_id = 'fbea320c'
app_key = '436eab6eae3dbeec6ec311328b9ff7dd'
language = 'en'
if request.form.get("Word")
return render_template("final.html")
and here is my final.html
<html lang="en">
<head>
<style>
input{
max-width: 350px;
}
select{
max-width: 400px;
}
#ABC {
background-color: black;
position: absolute;
width: 95%;
}
#imh {
width: 100%;
height: 50%;
}
.centered {
position: absolute;
top: 25%;
left: 50%;
transform: translate(-50%, -50%);
color: #ffffff;
text-align: center;
}
</style>
<title>Word Finder</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body>
<img src="/static/jumble.jpeg" id="imh">
<div class="centered"><font size="7">Jumble Solver</font><font size="4"><p>Unscramble words!</p></font></div>
<div class="container">
<br><div class="row">
<center><h1>Enter a Word</h1></center><br><br></div><div class="row">
<div class="inpot"><center>
<form method="post" action="/">
<div class="form-group">
{% if a == 0 %}
<input type="text" name="Word" id="Word" autofocus autocomplete="off" class="form-control" placeholder="Enter a Word" onclick="return IsEmpty()" value="{{ j }}"></div>
{% else %}
<input type="text" name="Word" id="Word" autofocus autocomplete="off" class="form-control" placeholder="Enter a Word"/></div>
{% endif %}
<div class="form-group">
</div><input class="btn btn-danger" type="submit" name="submit" value="submit"></button> <input class="btn btn-danger" name="submit" value="next" type="submit"></button></form>
<div class="row">
<div class=" text-center">
<h3>Search up any word!</h3>
<p>With our jumble solver you can unscramble any mix of letters!</p>
</div>
<div class="col-sm-4 text-center">
</div>
<div class="col-sm-4 text-center">
</div></div>
</div></div></font></body></html>
i'm pretty new to coding, and i can't figure out what's going wrong
I have tried to run your code, so I made some modifications to make it run on my python 3.7 in the windows operating system. The following modifications and corrections:
1- use input instead of raw_input
2- use:
f = open("words.txt", "r")
dictionary = f.readlines()
instead of file('words.txt')
3- and of course use print(something) instead of print something.
4- adding to the end of your code the run command:
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=1)
5- Indentation in the function def check(x, y): should be corrected `
6- Adding the missing : in if request.form.get("Word")
Finally, the program runs, but you use input from console like raw_input or input, meanwhile, you run Flask web server, so you should use forms to get the user inputs.
It would be good if add a sample input data and some lines of the file words.txt
Good Luck:

The Text passed through the flask render template to the html file is not formatting properly

I am trying to pass lyrics to the html file through flask but it is not formatting properly.
This is how I want it formatted, and I am passing it in the text variable in the same format -
The value of the text variable
This is how it is showing in the html website - The actual result
How do I get it formatted properly on the website as it is already passed in a formatted way.
This is the python code -
import flask
import subprocess
import sys
import generate_unconditional_samples as model
app = flask.Flask(__name__, template_folder='templates')
#app.route('/', methods=['GET', 'POST'])
def main():
if flask.request.method == 'GET':
return(flask.render_template('main.html'))
if flask.request.method == 'POST':
text = model.sample_model("Lyric", None, 0, 1, None, 0.8, 40, 0.0)
print(text)
return flask.render_template('main.html', result = text,)
if __name__ == '__main__':
app.run()
And This is the HTML code -
<!doctype html>
<html>
<style>
form {
margin: auto;
width: 35%;
}
.result {
margin: auto;
width: 100%;
border: 1px solid #ccc;
}
</style>
<head>
<title>Lyric Generator</title>
</head>
<body>
<form action="{{ url_for('main') }}" method="POST">
<fieldset>
<input type="submit">
</fieldset>
</form>
<div class="result" align="center">
{% if result %}
<br> Generated Lyrics:
<p style="font-size:20px">{{ result }}</p>
{% endif %}
</div>
<body>
</html>
I got the answer, just had to use the white-space : pre-line to spilt the lines
.result {
margin: center
width: 100%;
white-space: pre-line;
border: 1px solid #ccc;
}
The Final Result

XMLHttp request isn't being processed

I am attempting to take what a user highlights in order to process it for a query of a database. While I seem to be able to successfully get what is being highlighted, it seems that passing that information through to the actual database query isn't happening. There aren't any error messages. I hope there is a typo somewhere and it is an easy fix, in any scenario an extra set of eyes from a more experienced person would be greatly appreciated. I am using Python Flask to develop a web app for personal use, here is the code
{% block content %}
<head>
<style>
div.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
float: right;
width: 30%;
}
</style>
</head>
<h2 style="text-align: center;">{{ name }}</h2>
<div class="sticky" id="sticky_display">
<button type="button" onclick ="getSelectedText()" style="background: darkblue; color: white;">Query Database</button>
<form name="testform">
<textarea id="selectedtext">
</textarea>
</form>
</div>
<div class="row">
<div class="col-xs-8 col-sm-8 col-md-8 col-lg-8" style="white-space: pre-line;" id="text_display">
{{ text }}
</div>
</div>
<script>
function getSelectedText() {
var selectedText = '';
if (window.getSelection) {
selectedText = window.getSelection();
}
// document.getSelection
else if (document.getSelection) {
selectedText = document.getSelection();
}
// document.selection
else if (document.selection) {
selectedText = document.selection.createRange().text;
} else return;
function(selectedText) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function(){
if (this.readyState == 4 && this.status == 200) {
document.getElementById('selectedtext').value = this.responseText;
}
xhttp.open("POST", "/query_database', true);
xhttp.send(selectedText);
}
}
</script>
{% endblock %}
By default, POST will send
Content-Type: application/x-www-form-urlencoded
which requires that send a URL-encoded form. It appears that you're sending plain text, which would require adding
xhttp.setRequestHeader('Content-Type', 'text/plain');
before the .send()

Printing dynamic django view template

I'm working on a django app. I have a page that displays a log of items, and each item has a "Print label" link. At the moment, clicking the link displays the label for that particular item in a popup screen, but does not send the label to a printer. The view function behind the "Print label" link is shown below:
#login_required
def print_label(request, id):
s = Item.objects.get(pk = id)
return render_to_response('templates/label.html', {'s': s}, context_instance=RequestContext(request))
The HTML for the label is shown below:
{% load humanize %}
<head>
<style type="text/css">
div{
min-width: 350px;
max-width: 350px;
text-align: center;
}
body{
font-family: Arial;
width: 370px;
height: 560px;
text-align: center;
}
</style>
</head>
<body>
<div id="labelHeader">
<img src="{{ STATIC_URL }}img/label-header.png" width="350px">
</div>
<hr/>
<p></p>
<div id="destinationAddress">
<span style="font-size: xx-large; font-weight: bold;">{{ s.item_number }}</span>
</p>
DESTINATION:
<br/>
<strong>{{s.full_name}}</strong><br/>
<strong>{{ s.address }}</strong><br/>
<strong>{{s.city}}, {{s.state}}</strong><br/>
<strong>Tel: {{s.telephone}}</strong>
</div>
<p></p>
<hr/>
<div id="labelfooter">
<img src="{{ STATIC_URL }}img/label-footer.png" width="350px">
</div>
</body>
My question is, how can I also send the label displayed to a printer in the same function? I researched and found some libraries (like xhtml2pdf, webkit2png, pdfcrowd, etc), but they'll create a pdf or image file of the label and I'll have to send it to a printer. Is it possible to send straight to a printer without creating a pdf copy of the label? If so, please show me how to achieve this.
Your answers and suggestions are highly welcome. Thank you.
Presumably, as this is a Django app, it's the client's printer that you need to use. The only way to do this is to tell the user's browser to print. You will need to use Javascript for this: window.print().

Display a ‘loading’ message while a time consuming function is executed in Flask

I’m still relatively new to Flask, and a bit of a web noob in general, but I’ve had some good results so far. Right now I’ve got a form in which users enter a query, which is given to a function that can take anywhere between 5 and 30 seconds to return a result (looking up data with the Freebase API).
The problem is that I can’t let the user know that their query is loading during this time, as the results page only loads once the function finishes its work. Is there a way I can display a loading message while that's going on? I found some Javascript that could display a loading message while page elements are still loading, but my waiting period happens before ‘render_template’.
I knocked together some example code, just to demonstrate my situation:
Python:
from flask import Flask
from flask import request
from flask import render_template
import time
app = Flask(__name__)
def long_load(typeback):
time.sleep(5) #just simulating the waiting period
return "You typed: %s" % typeback
#app.route('/')
def home():
return render_template("index.html")
#app.route('/', methods=['POST'])
def form(display=None):
query = request.form['anything']
outcome = long_load(query)
return render_template("done.html", display=outcome)
if __name__ == '__main__':
#app.debug = True
app.run()
Excerpt from index.html:
<body>
<h3>Type anything:</h3>
<p>
<form action="." method="POST">
<input type="text" name="anything" placeholder="Type anything here">
<input type="submit" name="anything_submit" value="Submit">
</form>
</p>
</body>
Excerpt from done.html:
<body>
<h3>Results:</h3>
<p>
{{ display }}
</p>
</body>
Any help would be greatly appreciated, I hope this example helps.
Add this to your index.html or js file (I'm assuming you have jQuery here, you could use standard javascript of course.):
<script type="text/javascript">// <![CDATA[
function loading(){
$("#loading").show();
$("#content").hide();
}
// ]]></script>
Add this to you html or css file:
div#loading {
width: 35px;
height: 35px;
display: none;
background: url(/static/loadingimage.gif) no-repeat;
cursor: wait;
}
You can get an adequate GIF from http://www.ajaxload.info/. Download and put it into your static folder.
Then change your submission button to call above js function:
<input type="submit" name="anything_submit" value="Submit" onclick="loading();">
and add in a loading and a content div to you base html file:
<body>
<div id="loading"></div>
<div id="content">
<h3>Type anything:</h3>
<p>
<form action="." method="POST">
<input type="text" name="anything" placeholder="Type anything here">
<input type="submit" name="anything_submit" value="Submit" onclick="loading();">
</form>
</p>
</div>
</body>
Now when you click 'Submit', the js function should hide your content and display a loading GIF. This will display until your data is processed and flask loads the new page.
This can be done by using a div that contains a 'loading gif' image. When the submit button is clicked, the div is displayed using javascript.
To implement this, you can take a look at this website: http://web.archive.org/web/20181023063601/http://www.netavatar.co.in/2011/05/31/how-to-show-a-loading-gif-image-while-a-page-loads-using-javascript-and-css/
I found the purely CSS-dependent loader very useful. It does not depend on external resources:
https://www.w3schools.com/howto/howto_css_loader.asp
This is a bit of an old topic, but I needed to deal with this problem today and came with a solution on my own. I'm running a machine learning model that recieves an image input from the user and does some magic.
Basically this is what I did.
On my index.html file, I called a loading function on my "Submit" button passing the filename, because I was going to use it later:
<form method="post" action="/loading/{{filename}}"
enctype="multipart/form-data">
<input type="submit" value="Submit!">
</form>
On Flask, I created a route just to render the loading screen before doing the time consuming task, also passing the filename ahead:
#app.route('/loading/<filename>', methods=['POST'])
def loading_model(filename):
return render_template ("loading.html", filename=filename)
And then, on loading.html, I render my .gif animation and at the end I redirect the page to the time consuming task function:
<!doctype html>
<head>
<link rel= "stylesheet" type= "text/css" href= "{{url_for('static',filename='styles/main.css') }}">
</head>
<div id="preloader">
<div id="status">&nbsp</div>
<h1 class="ml13">ANALYZING...</h1>
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js> </script>
</div>
<title>Title</title>
<script src="{{url_for('static', filename='main.js')}}"></script>
<script> window.location.replace('/task/{{filename}}'); </script>
And then, final step, back to Flask, call the task function:
#app.route('/task/<filename>', methods=['POST', 'GET'])
def task(filename):
# Do your stuff
return render_template ("results.html")
By doing this, the gif animation will keep playing whilst the function does its job, and then render the results or the next page you want.
You obviously have to edit the css file so that "preloader" and "status" behave like you wish, this is how I used it:
#preloader {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
#status {
background-image: url("lalala.gif");
background-repeat: no-repeat;
width: 800px;
height: 600px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -400px;
margin-left: -400px;
}
It worked out for me.
Brilliant #jka.ne but confusing situation.
I only needed to introduce the loading gif while a button was clicked.
My solution was:
<script type="text/javascript">
function loading(){
$("#loading").show();
window.location.href="../target_html";
}
</script>
Then:
<button type="button" class="xxx" onclick="loading();">Run</button>
Finally:
<div id="loading"></div>

Categories

Resources