Not a very experienced user with python here, just picked up python while staying at home. I was looking into integrating the output of flask-table to a website. The following is my python code that creates a simple table for my website.
from flask_table import Table, Col
app = Flask(__name__)
class ItemTable(Table):
name = Col('Name')
description = Col('Description')
Class Item(object):
def __init__(self, name, description):
self.name = name
self.description = description
items = [dict(name='Name1', description='Description1'),
dict(name='Name2', description='Description2'),
dict(name='Name3', description='Description3')]
table = ItemTable(items)
#app.route("/")
def hello():
return render_template('index.html', tStrToLoad=table.__html__())
if __name__ == "__main__":
app.run()
and my html code that takes tStrToLoad from the python code above to display.
<html>
<head>
<title>Test Flask Table</title>
<style>
body
{
background-color: #000000;
color: #FFFFFF;
font-family:Verdana;
font-size:16px;
}
table, th, td
{
border: 1px solid #0088ff;
border-collapse: collapse;
padding: 3px;
font-family:Verdana;
font-size:12px;
text-align: left;
}
</style>
</head>
<body>
a simple test table
<br/><br/>
{{ tStrToLoad }}
</body>
</html>
And instead of showing a table with the data, I have the following output in a black background
a simple test table
<table> <thead><tr><th>Name</th><th>Description</th></tr></thead> <tbody> <tr><td>Name1</td><td>Description1</td></tr> <tr><td>Name2</td><td>Description2</td></tr> <tr><td>Name3</td><td>Description3</td></tr> </tbody> </table>
Upon further investigating, I did a view page source, this is my actual html code that arises from this.
<html>
<head>
<title>Test Flask Table</title>
<style>
body
{
background-color: #000000;
color: #FFFFFF;
font-family:Verdana;
font-size:16px;
}
table, th, td
{
border: 1px solid #0088ff;
border-collapse: collapse;
padding: 3px;
font-family:Verdana;
font-size:12px;
text-align: left;
}
</style>
</head>
<body>
a simple test table
<br/><br/>
<table>
<thead><tr><th>Name</th><th>Description</th></tr></thead>
<tbody>
<tr><td>Name1</td><td>Description1</td></tr>
<tr><td>Name2</td><td>Description2</td></tr>
<tr><td>Name3</td><td>Description3</td></tr>
</tbody>
</table>
</body>
</html>
My question is how do I format it correctly in my python script such that it sends < and > instead of & lt; and & gt;
If you're confident none of the data in the table has un-validated user input then tell Jinja not to escape the html:
{{ tStrToLoad | safe }}
However you may wish to avoid using Flask-tables, and just pass the items list yourself. The template code can become more generic if you also pass some headers in a separate list:
headers = ['Name','Description']
return render_template('index.html',
headers = headers,
objects = items)
Then manually build the table:
{% if objects %}
<table id='result' class='display'>
<thead>
<tr>
{% for header in headers %}
<th>{{header}}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for object in objects %}
<tr>
{% for k, val in object.items() %}
<td>{{val}}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
As you can see, this prevents you from having to hard-code any values into the template.
The syntax of this table is also compatible with datatables, so you if you load the CSS and JS files as per the zero configuration example then your table becomes prettier and can have a builtin search bar and pagination. Let me know if you need further direction on how to do this.
Related
I am trying to get data and file from the following HTML template:
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload Example</title>
<style>
body {
margin: 0;
}
.center {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
font-size: 16px;
}
</style>
</head>
<body>
<div class="center">
<form method="POST" action="/upload" enctype="multipart/form-data">
<table>
<tr>
<td class="lbl"><label for="sub_id">Subject Id:</label></td>
<td><input class="fld" id="sub_id" type="text" placeholder="01" required></td>
</tr>
<tr>
<td class="lbl"><label for="sub_first_name">First Name:</label></td>
<td><input class="fld" id="sub_first_name" type="text" placeholder="John" required></td>
</tr>
<tr>
<td class="lbl"><label for="sub_last_name">Last Name:</label></td>
<td><input class="fld" id="sub_last_name" type="text" placeholder="Appleceed" required></td>
</tr>
<tr>
<td class="lbl"><label for="files">File upload:</label></td>
<td><input class="fld" name="file" type="file"></td>
</tr>
</table>
<input type="submit" />
<!--<button type='submit' name='selectionsubmit'>Submit</button>//-->
</form>
</div>
</body>
</html>
I am parsing this through flask #app.route:
#app.route("/upload", methods=["GET", "POST"])
def home():
if request.method == "POST":
file = request.files["file"]
data = jsonify(request.form).json()
print(data, flush=True)
when I try to print data, it returns an empty dict. How can I extract the data from HTML with the request, to get, for example, the value of sub_last_name with something like data.form['sub_last_name'] after form submit? Thanks
The content of an input field is accessed via the name attribute of the specific element defined in the HTML template. The reason you only get the data of the file field is that you only defined this attribute for said input field.
To get the data of the other fields, specify an attribute name.
<input type="text" name="sub_last_name" />
You can then query the data as usual and known from the dict type within the endpoint. So you can also ask whether the respective key is included.
sub_last_name = request.form['sub_last_name']
If you are not sure whether the attribute will be passed, you want to specify a default value or convert the type on request, you can also use get. In this case no KeyError is raised, but None or the optional defined default value is returned if the name of the input field is not found.
The default return value is of type str. However, if you also pass the type attribute, the input will be converted as desired. If the type change fails, the default value is returned.
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!
I am building a invoice report template in html and using Weasyprint to generate it as a PDF(and as a docx eventually)
The issue I'm having is in the inability to not only page-break, but to also generate a running header and footer properly without the body contents overlapping and turning my data into zalgo texts.
My report template has this simple format:
+==========================+
+ Header +
+==========================+
+ Body +
+==========================+
+ Footer +
+==========================+
Both the header and footer will more or less be prevalent over the pages. The header includes a page counter while the footer will display a value within a textbox only on the last page.
Both my header and footer are referenced to separate HTML templates for versatility, using the include keyword to include them. As this is a template for an invoice, the header is more similar to a letter head.
The main content will be in the body. If the content is too much, it will break and continue on to the next page.
For all 3 parts, I am using tables for formatting purpose, mainly to keep my data aligned.
Here is a sample of my main HTML body:
<!DOCTYPE html>
<html>
<head>
<style type="text/css" media="all">
#page {
size: A4 portrait; /* can use also 'landscape' for orientation */
margin: 1cm;
#top-left{
content: element(header);
}
#bottom-left{
content: element(footer);
}
}
header, footer, .body_content{
font-size: 12px;
/* color: #000; */
font-family: Arial;
width: 100%;
position: relative;
}
header {
/*position: fixed;*/
/*position: running(header); */
/*display: block; */
}
footer {
position: fixed;
/*position: running(footer);*/
/*position: absolute;*/
bottom: 0;
/*display: block;*/
}
.body_content {
position: relative;
page-break-inside: auto;
height: 320pt;
/*overflow: hidden;*/
}
</style>
</head>
<body>
<header>
{% include 'sampleTemplate_header.html' %}
</header>
<div >
<table class="body_content">
<tbody >
<tr style="padding-top:5px;" >
<td style="width:60%;" >
</td>
<td style="width:10%;" >
</td>
<td style="width:15%;" >
</td>
<td style="width:15%;" >
</td>
</tr>
<tr>
</tr>
<tr >
<td style="width:60%;" >
</td>
<td style="width:10%;" >
</td>
<td style="width:15%;" align="right" >
</td>
<td style="width:15%;" align="center" >
</td>
</tr>
<tr >
<td style="width:60%;" id="testCell" >
</td>
<td style="width:10%;" >
</td>
<td style="width:15%;" >
</td>
<td style="width:15%;" >
</td>
</tr>
</tbody>
</table>
</div>
<footer>
{% include 'sampleTemplate_footer.html' %}
</footer>
</body>
</html>
The CSS portion has a lot of commented code due to my experimenting on the layout, but as much as I change, I cant seem to get the layout I need.
One of my most prevalent issue has been the overlapping text of the body content with the header or the footer. The later even happens, despite a forced page-break-after.
I got it working with the running elements (I was reading about it here: https://www.w3.org/TR/css-gcpm-3/#running-elements).
If you put the footer before the main content, then it will show on every page. Running elements apparently moves an element from the main flow into the margin, so I guess if it isn't in the page yet it can't move it into the margin.
To get the header/footer to stop overlapping with the contents, I had to play around with the margin value for the #page. Since running elements moves the element into the margin, making the margin bigger gives it more space. In the example below, if you decrease the value for top (or bottom) margin, the header (or footer) will overlap.
Sometimes I have to set the header/footer height value to get it to position in the margin properly, but I didn't have to for this example.
<!DOCTYPE html>
<html>
<head>
<style type="text/css" media="all">
#page {
size: A4 portrait; /* can use also 'landscape' for orientation */
margin: 100px 1cm 150px 1cm;
#top-left{
content: element(header);
}
#bottom-left{
content: element(footer);
}
}
header {
position: running(header);
/*height: 100px;*/
}
footer {
position: running(footer);
/*height: 150px;*/
}
</style>
</head>
<body>
<header>
multiline<br>header<br>lots<br>of<br>lines<br>here<br>
</header>
<footer>
multiline<br>footer<br>lots<br>of<br>lines<br>here
</footer>
<div >
stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff<br>stuff
</div>
</body>
</html>
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
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().