Python Variable in an HTML email in Python - python

How do I insert a variable into an HTML email I'm sending with python? The variable I'm trying to send is code. Below is what I have so far.
text = "We Says Thanks!"
html = """\
<html>
<head></head>
<body>
<p>Thank you for being a loyal customer.<br>
Here is your unique code to unlock exclusive content:<br>
<br><br><h1><% print code %></h1><br>
<img src="http://example.com/footer.jpg">
</p>
</body>
</html>
"""

Use "formatstring".format:
code = "We Says Thanks!"
html = """\
<html>
<head></head>
<body>
<p>Thank you for being a loyal customer.<br>
Here is your unique code to unlock exclusive content:<br>
<br><br><h1>{code}</h1><br>
<img src="http://example.com/footer.jpg">
</p>
</body>
</html>
""".format(code=code)
If you find yourself substituting a large number of variables, you can use
.format(**locals())

Another way is to use Templates:
>>> from string import Template
>>> html = '''\
<html>
<head></head>
<body>
<p>Thank you for being a loyal customer.<br>
Here is your unique code to unlock exclusive content:<br>
<br><br><h1>$code</h1><br>
<img src="http://example.com/footer.jpg">
</p>
</body>
</html>
'''
>>> s = Template(html).safe_substitute(code="We Says Thanks!")
>>> print(s)
<html>
<head></head>
<body>
<p>Thank you for being a loyal customer.<br>
Here is your unique code to unlock exclusive content:<br>
<br><br><h1>We Says Thanks!</h1><br>
<img src="http://example.com/footer.jpg">
</p>
</body>
</html>
Note, that I used safe_substitute, not substitute, as if there is a placeholder which is not in the dictionary provided, substitute will raise ValueError: Invalid placeholder in string. The same problem is with string formatting.

use pythons string manipulation:
http://docs.python.org/2/library/stdtypes.html#string-formatting
generally the % operator is used to put a variable into a string, %i for integers, %s for strings and %f for floats,
NB: there is also another formatting type (.format) which is also described in the above link, that allows you to pass in a dict or list slightly more elegant than what I show below, this may be what you should go for in the long run as the % operator gets confusing if you have 100 variables you want to put into a string, though the use of dicts (my last example) kinda negates this.
code_str = "super duper heading"
html = "<h1>%s</h1>" % code_str
# <h1>super duper heading</h1>
code_nr = 42
html = "<h1>%i</h1>" % code_nr
# <h1>42</h1>
html = "<h1>%s %i</h1>" % (code_str, code_nr)
# <h1>super duper heading 42</h1>
html = "%(my_str)s %(my_nr)d" % {"my_str": code_str, "my_nr": code_nr}
# <h1>super duper heading 42</h1>
this is very basic and only work with primitive types, if you want to be able to store dicts, lists and possible objects I suggest you use cobvert them to jsons http://docs.python.org/2/library/json.html and https://stackoverflow.com/questions/4759634/python-json-tutorial are good sources of inspiration
Hope this helps

Related

adding string variables to HTML body in Python without breaking content-ID

I'm trying to make an automated task that (1) collects CSV data about wildfires from the internet, (2) reads its contents to see where wildfires in the CSV have occurred in the past 3 days, (3) visualizes them on a map, (4) and sends an email to a specific alias, with text and a map about where they have occurred.
In the email (4), I want to mention the locations of wildfires, which are in the form of a string:
print(provinces_on_fire_str)
Out[0]: "Province1, Province2"
I used Content-ID to add an image to the e-mail body, using the following code:
# set the plaintext body
msg.set_content('This is a plain text body.')
# create content-ID for the image
image_cid = make_msgid()
# alternative HTML body
msg.add_alternative("""\
<html>
<body>
<p>This e-mail contains information about fires reported in the past 3 days.<br>
The VIIRS sensor has reported wildfires in the following provinces: {provinces_on_fire_str}
This e-mail was sent automatically.
</p>
<img src="cid:{image_cid}">
</body>
</html>
""".format(image_cid=image_cid[1:-1]), subtype="html")
# attach image to mail
with open(f"path/toimage/{today}.png", 'rb') as img:
maintype, subtype = mimetypes.guess_type(img.name)[0].split("/")
msg.get_payload()[1].add_related(img.read(),
maintype=maintype,
subtype=subtype,
cid=image_cid)
This returns an error, implying no such object as "provinces_on_fire_str" exists for the HTML code. Without the "provinces_on_fire_str" variable in the HTML body, the expected output email is the following (albeit this lacks the text explanation of where they occurred):
Now, the obvious thing that came to my mind is to convert the HTML body part to an f-string, so I can add the "Province1, Province2" values to the e-mail text. But adding f before the e-mail string breaks the image_cid (though the Province1, Province2 values are included in the ultimate e-mail).
.add_alternative with f-string input:
# alternative HTML body
msg.add_alternative(f"""\
<html>
<body>
<p>This e-mail contains information about fires reported in the past 3 days.<br>
The VIIRS sensor has reported wildfires in the following provinces: {provinces_on_fire_str}
This e-mail was sent automatically.
</p>
<img src="cid:{image_cid}">
</body>
</html>
""".format(image_cid=image_cid[1:-1]), subtype="html")
Output email:
How do I pass the string values of provinces_on_fire_str into the HTML code without breaking the image_cid?
Consider a simpler example:
foo = 1
bar = 2
print('{foo} == {bar}'.format(foo=foo+1))
It does not work, because bar is not looked up automatically. The .format method of strings is just a method; it does not do any magic. It does not know about the caller's local variables foo and bar; it must be passed all the information that should be used. Of course, because we are passing it information explicitly, we can make modifications.
We can solve the error by simply including the missing argument:
foo = 1
bar = 2
print('{foo} == {bar}'.format(foo=foo+1, bar=bar))
f-strings are magic, or rather, syntactic sugar. They are translated into an equivalent .format call at compile time. They are not a different kind of string; after the compile-time translation, a perfectly ordinary string has a perfectly ordinary .format method called upon it.
If we do
foo = 1
bar = 2
print(f'{foo+1} == {bar}')
then that is already equivalent to the fixed .format version. We can use expressions in the {} placeholders, not just variable names. Notice that this already does the work; we should not have an explicit .format call on the result.
If we have just
foo = 1
bar = 2
print(f'{foo} == {bar}')
then of course we lose the modification of the foo value. If you want to use a modified foo in the formatted output, then either describe the modification in the f-string, or else modify the variable beforehand.
Translating that to the original code, we can either do:
msg.add_alternative(f"""\
<html>
<body>
<p>This e-mail contains information about fires reported in the past 3 days.<br>
The VIIRS sensor has reported wildfires in the following provinces: {provinces_on_fire_str}
This e-mail was sent automatically.
</p>
<img src="cid:{image_cid[1:-1]}">
</body>
</html>
""")
or:
image_cid = image_cid[1:-1]
msg.add_alternative(f"""\
<html>
<body>
<p>This e-mail contains information about fires reported in the past 3 days.<br>
The VIIRS sensor has reported wildfires in the following provinces: {provinces_on_fire_str}
This e-mail was sent automatically.
</p>
<img src="cid:{image_cid}">
</body>
</html>
""")

HTML string in python file doesn't execute as expected

When I type the code below, it gives me a blank HTML page. Even though I put a <h1> and a <a href> tag. Only the <title> tag is executed. Does anyone know why and how to fix it?
Code:
my_variable = '''
<html>
<head>
<title>My HTML File</title>
</head>
<body>
<h1>Hello world!</h1>
Click me
</body>
</html>'''
my_html_file = open(r"\Users\hp\Desktop\Code\Python testing\CH\my_html_file.html", "w")
my_html_file.write(my_variable)
Thanks in advance!
As #bill Bell said, it's probably because you haven't closed your file (so it hasn't flushed its buffer).
So, in your case:
my_html_file = open(r"\Users\hp\Desktop\Code\Python testing\CH\my_html_file.html", "w")
my_html_file.write(my_variable)
my_html_file.close()
But, this is not the right way to do it. Indeed, if an errors occurs in the second line for example, the file'll never get closed. So, you can use the with statement to make sure that it always is. (just as #Rawing said)
with open('my-file.txt', 'w') as my_file:
my_file.write('hello world!')
So, in fact, it's like if you did:
my_file = open('my-file.txt', 'w')
try:
my_file.write('hello world!')
finally:
# this part is always executed, whatever happens in the try block
# (a return, an exception)
my_file.close()

Using pybtex to convert from bibtex to formatted HTML bibliography in e.g. Harvard style

I'm using Django and am storing bibtex in my model and want to be able to pass my view the reference in the form of a formatted HTML string made to look like the Harvard reference style.
Using the method described in Pybtex does not recogonize bibtex entry it is possible for me to convert a bibtex string into a pybtex BibliographyData object. I believe it should be possible to get from this to an HTML format based on the docs https://pythonhosted.org/pybtex/api/formatting.html but I just don't seem to be able to get it working.
Pybtex seems to be set up to be used from the command line rather than python, and there are very few examples of it being used on the internet. Has anyone done anything like this? Perhaps it would be easier to pass the bibtex to my template and use a javascript library like https://github.com/pcooksey/bibtex-js to try and get an approximation of the Harvard style?
To do that I adapted some code from here. I am not sure what is the name of this particular formatting style, but most probably you can change/edit it. This is how it looks:
import io
import six
import pybtex.database.input.bibtex
import pybtex.plugin
pybtex_style = pybtex.plugin.find_plugin('pybtex.style.formatting', 'plain')()
pybtex_html_backend = pybtex.plugin.find_plugin('pybtex.backends', 'html')()
pybtex_parser = pybtex.database.input.bibtex.Parser()
my_bibtex = '''
#Book{1985:lindley,
author = {D. Lindley},
title = {Making Decisions},
publisher = {Wiley},
year = {1985},
edition = {2nd},
}
'''
data = pybtex_parser.parse_stream(six.StringIO(my_bibtex))
data_formatted = pybtex_style.format_entries(six.itervalues(data.entries))
output = io.StringIO()
pybtex_html_backend.write_to_stream(data_formatted, output)
html = output.getvalue()
print (html)
This generates the following HTML formatted reference:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head><meta name="generator" content="Pybtex">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Bibliography</title>
</head>
<body>
<dl>
<dt>1</dt>
<dd>D. Lindley.
<em>Making Decisions</em>.
Wiley, 2nd edition, 1985.</dd>
</dl></body></html>
I've notice the command line pybtex-format tool produces a fair output for HTML:
$ pybtex-format myinput.bib myoutput.html
So I went to the source code at pybtex/database/format/__main__.py and found an incredibly simple solution that worked like a charm for me:
from pybtex.database.format import format_database
format_database('myinput.bib', 'myoutput.html', 'bibtex', 'html')
Here are my input and output files:
#inproceedings{Batista18b,
author = {Cassio Batista and Ana Larissa Dias and Nelson {Sampaio Neto}},
title = {Baseline Acoustic Models for Brazilian Portuguese Using Kaldi Tools},
year = {2018},
booktitle= {Proc. IberSPEECH 2018},
pages = {77--81},
doi = {10.21437/IberSPEECH.2018-17},
url = {http://dx.doi.org/10.21437/IberSPEECH.2018-17}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head><meta name="generator" content="Pybtex">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Bibliography</title>
</head>
<body>
<dl>
<dt>1</dt>
<dd>Cassio Batista, Ana Larissa Dias, and Nelson <span class="bibtex-protected">Sampaio Neto</span>.
Baseline acoustic models for brazilian portuguese using kaldi tools.
In <em>Proc. IberSPEECH 2018</em>, 77–81. 2018.
URL: http://dx.doi.org/10.21437/IberSPEECH.2018-17, doi:10.21437/IberSPEECH.2018-17.</dd>
</dl></body></html>

Programmatically delete everything before a HTML node?

I am trying to create a corpus of data from a set of .html pages I have stored in a directory.
These HTML pages have lots of info I don't need.
This info is all stored before the line
<div class="channel">
How can I programmatically remove all of the text before
<div class="channel">
in every HTML file in a folder?
Bonus question for a 50point bounty :
How do I programmatically remove everything AFTER, for example,
<div class="footer">
?
So if my index.html was previously :
<head>
<title>This is bad HTML</title>
</head>
<body>
<h1> Remove me</h1>
<div class="channel">
<h1> This is the good data, keep me</h1>
<p> Keep this text </p>
</div>
<div class="footer">
<h1> Remove me, I am pointless</h1>
</div>
</body>
After my script runs, I want it to be :
<div class="channel">
<h1> This is the good data, keep me</h1>
<p> Keep this text </p>
</div>
This is a bit heavy on memory usage, but it works. Basically you open up the directory, get all ".html" files, read them into a variable, find the split point, store the before or after in a variable, and then overwrite the file.
There are probably better ways to do this, nonetheless, but it works.
import os
dir = os.listdir(".")
files = []
for file in dir:
if file[-5:] == '.html':
files.insert(0, file)
for fileName in files:
file = open(fileName)
content = file.read()
file.close()
loc = content.find('<div class="channel">')
newContent = content[loc:]
file = open(fileName, 'w')
file.write(newContent)
file.close()
If you wanted to just keep up to a point:
newContent = content[0:loc - 1] # I think the -1 is needed, not sure
Note that the things you're searching should be kept in a variable, and not hardcoded.
Also, this won't work recursively for file/folder structures, but you can find out how to modify it to do that very easily.
to remove everything above and everything below
that means the only thing left should be this section:
<div class="channel">
<h1> This is the good data, keep me</h1>
<p> Keep this text </p>
</div>
rather than thinking to remove the unwanted, it would be easier to just extract the wanted.
you can easily extract channel div using XML parser such as DOM
You've not mentioned a language in the question - the post is tagged with python so this answer might still be out of context, but I'll give a php solution that could likely easily be rewritten in another language.
$html='....'; // your page
$search='<div class="channel">';
$components = explode($search,$html); // [0 => before the string, 1 => after the string]
$result = $search.$components[1];
return $result;
To do the reverse is fairly easy too; simply take the value of $components[0] after altering $search to your <div class="footer"> value.
If you happen to have the $search string cropping up multiple times:
$html='....'; // your page
$search='<div class="channel">';
$components = explode($search,$html); // [0 => before the string, 1 => after the string]
unset($components[0]);
$result = $search.implode($search,$components);
return $result;
Someone who knows python better than I do feel free to rewrite and take the answer!

"%" character cause Error in strings substitution with locals()

I try to substitue strings with variables using locals() in python but I can find a way to use the % character inside the string without error. Here is a concrete example :
color = colors_generator() #the function return a color
html = """<html><head>
<style>#square{color:%(color)s;width:100%;height:100%;}</style>
</head> <body> <div id="square"> </div>
</body></html>""" % locals()
print "Content-Type: text/html\n"
print html
Result : TypeError: not enough arguments for format string
The problem is the % character in 100%. How can I escape it?
escape % with %
html = """<html><head>
<style>#square{color:%(color)s;width:100%%;height:100%%;}</style>
</head> <body> <div id="square"> </div>
</body></html>""" % locals()
Virhilo has already answered your direct question, but if you find you are building quite big/complicated templates it might be worth looking at a full blown template engine instead:
http://docs.python.org/library/string.html
http://jinja.pocoo.org/
http://www.makotemplates.org/

Categories