Adding multiple images to a html file using Jinja2 - html

I am trying to load and add multiple figures from disk to a HTML file
For adding a single image, I tried the following (ref.: Jinja2/Python insert a image(s) into html)
import jinja2
env = jinja2.Environment(
loader=jinja2.FileSystemLoader('.'),
trim_blocks=True,
lstrip_blocks=True,
)
template = env.get_template("template.html")
template_vars = {"title":"TITLE", "graph":'obj.png'}
text = template.render(template_vars)
with open("test2.html", "w") as f_out:
f_out.write(text)
The template looks like the below
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>TITLE</title>
</head>
<body>
<h2>Graph Goes Here</h2>
<img src="obj.png">
</body>
</html>
To extend this to add multiple images, I made the following modification in the template
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h2>Graph Goes Here</h2>
<dl>
{% for key, value in template_vars.items() %}
<img src="{{value}}.png">
{% endfor %}
</dl>
</body>
</html>
I would like to know how
template_vars = {"title":"TITLE", "graph":'obj.png'}
has to be modified to pass multiple figures.

You will need to update the object type used for your variable template_vars to make things simpler to iterate. Instead of a dictionary, you likely want a list of dictionaries so that you will be able to loop over each image and get multiple attributes for each one (in your case, a value for the key title and another one for the key graph). You should also refer to your variable when calling template.render so that Jinja2 knows what you are writing about (i.e., replace template.render(template_vars) with template.render(template_vars=template_vars)).
Your updated template might look like this:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>TITLE</title>
</head>
<body>
<h2>Graph Goes Here</h2>
<dl>
{% for image in template_vars %}
<dt>{{ image.title }}</dt>
<dd><img title="{{ image.title }}" src="{{ image.graph }}"></dd>
{% endfor %}
</dl>
</body>
</html>
Then, the part of your Python code that needs to be changed would look as follows:
template_vars = [
{"title": "TITLE", "graph": "obj.jpg"},
{"title": "TITLE2", "graph": "obj2.jpg"},
]
text = template.render(template_vars=template_vars)
This will result in the following HTML source output:
<html>
<head lang="en">
<meta charset="UTF-8">
<title>TITLE</title>
</head>
<body>
<h2>Graph Goes Here</h2>
<dl>
<dt>TITLE</dt>
<dd><img title="TITLE" src="obj.jpg"></dd>
<dt>TITLE2</dt>
<dd><img title="TITLE2" src="obj2.jpg"></dd>
</dl>
</body></html>

Related

python flask create <div> for each directory

How do I create a div for each directory using flask?
I tried this but it does not do anything and theres nothing in the console:
python file:
# Get a list of directories in the 'servers' directory
folders = [d for d in os.listdir('servers/') if os.path.isdir(d)]
# Create a div element for each folder
divs = []
for folder in folders:
div = f"<div class='list'><a href='/servers/{folder}'>{folder}</a></div>"
divs.append(div)
# Join the div elements into a single string
divs_string = "\n".join(divs)
# Render the template and pass the div elements as a variable
return render_template('home.html')
html file (home.html):
<!DOCTYPE html>
<html lang="en">
<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">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/home.css') }}" />
<link rel="stylesheet" href="https://rawcdn.githack.com/CrystalVortex/Feather-CSS/9318334ceedfa61d6a64349a558ef1e48ef19cb2/Feather1.7.css">
<title>FeatherPanel | Home</title>
</head>
<body>
<form action="/create" method="post">
<button class="btn_blue">Create Server</button>
</form>
{% for directory in directories %}
<div>{{ directory }}</div>
{% endfor %}
</body>
</html>
The listdir function just returns a list of filenames. To test within your loop whether it is a directory, the isdir function expects the path including the folder name.
You can then pass the returned list to the template.
I'm using locals() here to pass all local variables to the template. However, you can also pass the variables individually with a key.
#app.route('/servers/')
def servers():
# Get a list of directories in the 'servers' directory
folders = [d for d in os.listdir('servers') if os.path.isdir(os.path.join('servers', d))]
# Render the template and pass the div elements as a variable
return render_template('home.html', **locals())
Within the template you can iterate over said list to create a "div" element for each entry.
<!DOCTYPE html>
<html lang="en">
<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">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/home.css') }}" />
<link rel="stylesheet" href="https://rawcdn.githack.com/CrystalVortex/Feather-CSS/9318334ceedfa61d6a64349a558ef1e48ef19cb2/Feather1.7.css">
<title>FeatherPanel | Home</title>
</head>
<body>
<form action="/create" method="post">
<button class="btn_blue">Create Server</button>
</form>
{% for folder in folders %}
<div>{{ folder }}</div>
{% endfor %}
</body>
</html>
You didn't describe what should happen when a user clicks a link for a listed directory. Keep in mind that only files in the static folder are accessible from the client. If you want to use an anchor to refer to listed directories outside of the static folder, you need another endpoint.

Convert a html with images to pdf

This is a follow up to my previous question
Adding multiple images to a html file using Jinja2
I am trying to save the html created using jinja2 to pdf.
import jinja2
env = jinja2.Environment(
loader=jinja2.FileSystemLoader('.'),
trim_blocks=True,
lstrip_blocks=True,
)
template = env.get_template("template.html")
template_vars = [
{"title": "TITLE", "graph": "obj.jpg"},
{"title": "TITLE2", "graph": "obj2.jpg"},
]
#template_vars = {"title":"TITLE", "graph":'obj.png'}
text = template.render(template_vars)
with open("test.html", "w") as f_out:
f_out.write(text)
Template:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>TITLE</title>
</head>
<body>
<h2>Graph Goes Here</h2>
<dl>
{% for image in template_vars %}
<dt>{{ image.title }}</dt>
<dd><img title="{{ image.title }}" src="{{ image.graph }}"></dd>
{% endfor %}
</dl>
</body>
</html>
To convert the html created in the above code, I tried to use pdfkit
pdfkit.from_file('test.html', 'test.pdf')
But this didn't work for me; I get the following error
Error:
File "C:\Users\xxxx\anaconda3\envs\pancreas_model\lib\site-packages\pdfkit\pdfkit.py", line 155, in handle_error
raise IOError('wkhtmltopdf reported an error:\n' + stderr)
OSError: wkhtmltopdf reported an error:
Exit with code 1 due to network error: ProtocolUnknownError
I also tried xhtml2pdf and this fails due to the image paths
with open('test.pdf', "w+b") as f_out:
pisa.CreatePDF(
src=text, # HTML to convert
dest=f_out)
WARNING:xhtml2pdf:Need a valid file name!
''
Could someone please suggest how to convert the html with multiple images to pdf?
I am not sure if I have to specify a template for the pdf output like the one specified
for html.
EDIT:
The relative path is specified in the template in the following line
<dd><img title="{{ image.title }}" src="./{{ image.graph }}"></dd>

Gin gonic templates overwriting partial templates

I am using gin gonic and it's features. One if them being html template rendering.
So in spirit of DRY I wanted to create a base.html template with all common html tags etc. with a
slot for different page bodies.
In essence, this is the base.html
{{define "base"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
{{ template "main" . }}
</body>
</html>
{{end}}
Then I created a "child" template called home.html:
{{template "base" .}}
{{define "main"}}
<div class="container mt-5">
Hello
</div>
{{end}}
I followed this wonderful guide on this page and it worked like a charm.
The problem
But when I tried adding another page with different body in subpage.html eg:
{{template "base" .}}
{{define "main"}}
<div class="container">
<div>
<h2>This page is still in progress</h2>
</div>
</div>
{{end}}
the last template that is picked by gins LoadHTMLFiles or LoadHTMLGlob, is then displayed on every page. In this case this is subpage.html content.
How do I fix this. Is it even possible to achieve this behaviour by default?
You could do something like this:
base.html
{{ define "top" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
{{ end }}
{{ define "bottom" }}
</body>
</html>
{{ end }}
home.html
{{ template "top" . }}
<div class="container mt-5">
Hello
</div>
{{ template "bottom" . }}
subpage.html
{{ template "top" . }}
<div class="container">
<div>
<h2>This page is still in progress</h2>
</div>
</div>
{{ template "bottom" . }}
Then make sure you're using the file's base name:
// in the home handler use the following
c.HTML(http.StatusOK, "home.html", data)
// in the subpage handler use the following
c.HTML(http.StatusOK, "subpage.html", data)

HTML5: Emmet works wrong if I use a smile in my text

Windows 10, Sublime Text Editor 3, Emmet.
My html file has UTF-8 encoding. I want to insert a smile in the text (I inserted the smile by Win + . keys pressing):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
p{A smile 😜}
</body>
</html>
After I wrote p{A smile 😜} text I pressed Tab key but the string wasn't transformed to html code. Hm... I pressed Tab key again and I got the expected result.
Ok... But now I will get the problem in Emmet. I added the text (ol):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<p>A smile 😜</p>
ol>li*5
</body>
</html>
Now, if I press the Tab key then I get the invalid result:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<p>A smile 😜</p>
o<ol>
<li></li>
</ol>
</body>
</html>
Pay attention to o before <ol> tag. Also, li item is one, but I expected to get 5 li items... I have the problem for some smiles. For example, I get the problem for 😜 smile, but I haven't for ☠.
const a = '😜';
console.log(`a.length: ${a.length}`); // 2
const b = '☠';
console.log(`b.length: ${b.length}`); // 1
How can I fix it?

Inherit/extend equivalent in handlebars

I am working on a static template and decided to use handlebars and gulp to generate the html part.
I have read several examples and I understand the concept of importing a partial like:
partial.hbs
<h1>Contents of the partial</h1>
index.hbs
<html>
<head></head>
<body>
{{> partial }}
</body>
</html>
which would yield:
<html>
<head></head>
<body>
<h1>Contents of the partial</h1>
</body>
</html>
What I can't seem to find is extending a base file instead of importing into it. What I mean is this:
base.hbs
<html>
<head></head>
<body>
{{ contents }}
</body>
</html>
index.hbs
{{extend base}}
{{ contents }}
<h1>Contents of the partial</h1>
{{ end contents }}
What is the proper way to do this in handlebars?