Attempting to write multiple pandas dataframes to an email and send it - python

I was able to create two dataframe lists and translate them into HTML, but I would like to know how to include them in order so they will populate my email I intend to send each day. I have updated my code and question on this post. For each {0), {1}, etc I would like to insert a dataframe. Is that possible?
import datetime
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
import pandas as pd
import matplotlib
ALL_DFS = [L_MUNIC_s, L_MUNIF_s, L_CAMNY_s, L_NYMNY_s]
ALL_DFS_html= ''
for ALL_DF in ALL_DFS:
ALL_DFS_html += ALL_DF.render()
ALL_DFS2 = [L_MUNIC_NAV, L_MUNIF_NAV, L_MUNIC5, L_MUNIF5]
ALL_DFS_html2= ''
for ALL_DF2 in ALL_DFS2:
ALL_DFS_html2 += ALL_DF2.to_html()
def send_mail():
dataf1 = ALL_DF
dataf2 = ALL_DF2
sender = "<name>#gmail.com"
receiver = ['<name>#gmail.com']
msg = MIMEMultipart('related')
today = datetime.date.today()
msg['Subject'] = "Daily Email " +
str(today.strftime("%m/%d/%y"))
msg['From'] = sender
msg['To'] = ", ".join(receiver)
html = """\
<html>
<head></head>
<body>
<p>Good Morning Team!<br>
L-MUC
NET ASK: {0}
Exposure:<br>
{1}
<br>Top 5:<br>
{2}
<br>
<br>NEW:<br>
{3}
<br>
</p>
<p>
L-MUF
NET ASK: {4}
Exposure:<br>
{5}
<br>Top 5:<br>
{6}
<br>
<br>NEW:<br>
{7}
<br>
</p>
<p>
L-CAY
NEW ASK: {8}
Exposure:<br>
{9}
<br>NEW:<br>
{10}
<br>
</p>
<p>
L-NYY
NEW ASK: {11}
Exposure:<br>
{12}
<br>NEW:<br>
{13}
<br>
</p>
</body>
</html>
""".format(dataf1.render(), dataf2.to_html())
partHTML = MIMEText(html, 'html')
msg.attach(partHTML)
ser = smtplib.SMTP('gateway_server', port_number)
ser.login("username", "password")
ser.sendmail(sender, receiver, msg.as_string())
return send_mail()

If the link provided in the comments doesn't answer your question, I wonder if this could solve your problems.
# Let's assume you have some list of dataframes.
dfs = [pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]}) for _ in range(4)]
dfs_html = ''
for df in dfs:
dfs_html += df.to_html()
dfs_html is now a nicely formatted bit of html with all your data.

Related

Sending a pandas Dataframe using smtplib

I've seen a lot of threads here about this topic, however, none regarding this specific question.
I am sending a email with a pandas dataframe (df) as an html using pandas built in df.to_html() method. The email sends successfully. However, the df is displayed in the email as html, not in the desired table format. Can anyone offer assistance on how to ensure the df is displayed as a table, not in html in the email? The code is below:
import requests
import pandas as pd
import smtplib
MY_LAT =
MY_LNG =
API_KEY = ""
parameters = {
"lat": MY_LAT,
'lon': MY_LNG,
'exclude': "",
"appid": API_KEY
}
df = pd.read_csv("OWM.csv")
response = requests.get("https://api.openweathermap.org/data/2.5/onecall", params=parameters)
response.raise_for_status()
data = response.json()
consolidated_weather_12hour = []
for i in range(0, 12):
consolidated_weather_12hour.append((data['hourly'][i]['weather'][0]['id']))
hour7_forecast = []
for hours in consolidated_weather_12hour:
weather_id = df[df.weather_id == hours]
weather_description = weather_id['description']
for desc in weather_description.iteritems():
hour7_forecast.append(desc[1])
times = ['7AM', '8AM', '9AM', '10AM', '11AM', '12PM', '1PM', '2PM', '3PM', '4PM', '5PM', '6PM']
col_header = ["Description of Expected Weather"]
weather_df = pd.DataFrame(data=hour7_forecast, index=times, columns=col_header)
my_email = ""
password = ""
html_df = weather_df.to_html()
with smtplib.SMTP("smtp.gmail.com", 587) as connection:
connection.starttls() # Makes connection secure
connection.login(user=my_email, password=password)
connection.sendmail(from_addr=my_email, to_addrs="",
msg=f"Subject: 12 Hour Forecast Sterp"
"""\
<html>
<head></head>"
<body>
{0}
<body>
</html>
""".format(html_df))
just use df.to_html() to convert it into an html table that you can include in your html email
then when you send the mail you must set the mimetype to html
smtp = smtplib.SMTP("...")
msg = MIMEMultipart('alternative')
msg['Subject'] = subject_line
msg['From'] = from_addr
msg['To'] = ','.join(to_addrs)
# Create the body of the message (a plain-text and an HTML version).
part1 = MIMEText(plaintext, 'plain')
part2 = MIMEText(html, 'html')
smtp.sendmail(from_addr, to_addrs, msg.as_string())
you can use the library html2text to convert your html to markdown for clients that do not support html content (not many these days) if you do not feel like writing the plaintext on your own
as an aside... using jinja when you are working with html tends to simplify things...

Sending two Pandas dataframes side-by-side using HTML in email

I'm trying to send some summary on my shares portfolio creation via email. I'm using Python + Pandas for the calculations and email.mime module to send html via email.
I am using Pandas to_html method and email.mime module to include the html in the email:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import numpy as np
import pandas as pd
import pickle
from utils import mail
def send_fancy_mail(subject, text_message, html_message):
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = 'mymail#domain.com'
msg['To'] = settings.MAIL_RECIPIENTS
# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text_message, 'plain')
part2 = MIMEText(html_message, 'html')
msg.attach(part1)
msg.attach(part2)
# Send the message via our own SMTP server.
s = smtplib.SMTP_SSL(settings.SMTP_HOST)
s.login(settings.SMTP_USER, settings.SMTP_PASSWORD)
s.send_message(msg)
s.quit()
fake_or_load = 'fake'
if fake_or_load == 'fake':
n_signals = 20
symbols = np.round(1000*np.random.rand(n_signals)).astype(int)
direction = ["Buy" for i in range(n_signals//2)]
direction.extend(["Sell" for i in range(n_signals//2)])
quantity = np.round(10000*np.random.rand(n_signals)).astype(int)
portfolio = pd.DataFrame({'symbols': symbols, 'direction': direction, 'quantity': quantity})
elif fake_or_load == 'load':
with open('c:\\\\temp\\signals_list', 'rb') as fp:
signals = pickle.load(fp)
portfolio = pd.DataFrame(signals)
portfolio.rename(index=str, inplace=True, columns={0: "symbol", 1: "direction", 2: "quantity"})
shares_to_buy = portfolio[portfolio['direction'] == 'Buy'].copy()
shares_to_buy.sort_values(by='quantity', inplace=True, ascending=False)
shares_to_sell = portfolio[portfolio['direction'] == 'Sell'].copy()
shares_to_sell.sort_values(by='quantity', inplace=True, ascending=False)
# The basic way to convert portfolio to html:
html_to_buy = shares_to_buy.to_html(index=False, header=True, col_space=20, justify='center')
html_to_sell = shares_to_sell.to_html(index=False, header=True, col_space=20, justify='center')
mail_body = "Test Message"
css = """
.div {
flex-direction: row;
}
"""
html_body = """
<html>
<head>
<style>{}</style>
</head>
<body>
<div>{}</div>
<div>{}</div>
</body>
</html>
""".format(css, html_to_buy, html_to_sell)
mail.send_fancy_mail("Test Mail", mail_body, html_body)
I would like to get two columns side-by-side in the email body, preferably able to regulate the column width as well. Is it possible to make the tables responsive?
If you want to create two columns, replace the two divs with the below table. Div's dont have the same support as tables on all email clients.
<table style="width:100%">
<tr>
<td>column 1</td>
<td>column 2</td>
</tr>
</table>

Removing the nan in python in an email

I have created an auto email using python. Right now,I am trying to remove the nan details in my email as user don't want nan to be in the email. I'm not sure how to do it. Can anyone help me to add or edit the code below
This is my code :
import pandas as pd
import csv
from tabulate import tabulate
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
user= 'mahirah.abd.wahab#ericsson.com'
password = '--------'
server = 'smtp.office365.com:587'
recipient = 'nur.suraya.shazwani.kamaru.zaman#ericsson.com,mahirah.abd.wahab#ericsson.com'
text = """
This is a test for the data to be sent
{table}
Regards,
Me"""
html = """
<html>
<head>
<style>
This is a test auto generated email
table, th, td {{ border: 1px solid black; border-collapse: collapse; }}
th, td {{ padding: 5px; }}
</style>
</head>
<body>
{table}
<p></p>
<p></p>
</body></html>
"""
# with open('input.csv') as input_file:
# reader = csv.reader(input_file)
# data = list(reader)
df = pd.read_csv('testcsv2.csv')
col_list = list(df.columns.values)
to_drop = []
for i in range(len(df)):
if df['HOSS1 REVIEWED DATE'][i] == 'nan':
to_drop.append(i)
df.drop(df.index[to_drop])
df.reset_index(drop=True, inplace=True)
data = df
# above line took every col inside csv as list
text = text.format(table=tabulate(data, headers=col_list, tablefmt="grid"))
html = html.format(table=tabulate(data, headers=col_list, tablefmt="html"))
message = MIMEMultipart(
"alternative", None, [MIMEText(text), MIMEText(html,'html')])
message['Subject'] = "Rental PR – Pending HOS Approval"
message['From'] = user
message['To'] = recipient
server = smtplib.SMTP(server)
server.ehlo()
server.starttls()
server.login(user, password)
server.sendmail(user, recipient, message.as_string())
server.quit()
I also tried using this one line code , but still doesn't work :
df = pd.dropna(subset=['HOSS1 REVIEWED DATE'])
THis is my output
Appreciate if anyone can help.
try this...
to_drop = []
for i in range(len(df)):
if df['HOSS1 REVIEWED DATE'][i] == 'nan':
to_drop.append(i)
df.drop(df.index[to_drop], inplace=True)
df.reset_index(drop=True, inplace=True)

Adding colors to individual columns of a dataframe when sending dataframe as table in an email?

Color individual columns while sending data frame over email
I have a data frame that i am emailing as a table with an attachment and the code works nice and dandy, but i now require to color some specific columns with a different color, i tried changing the html part of rendering table and also tried the Style functionality of pandas data frame but to no avail. Here is my code:
from tabulate import tabulate
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
import pandas as pd
from email.mime.base import MIMEBase
from email import encoders
me = 'myemail#gmail'
password = 'mypassword'
server = 'smtp.gmail.com:587'
you = 'myreceipient#gmail.com'
text = """
Hello, Friend.
Here is your data:
{table}
Regards,
Me"""
# here i tried to directly apply a color style into the table via <p> tag
html = """
<html>
<head>
<style>
table, th, td {{ border: 1px solid black; border-collapse: collapse; }}
th, td {{ padding: 5px; }}
</style>
</head>
<body><p>Hello, Friend This data is from a data frame.</p>
<p>Here is your data:</p>
<p style = "color: yellow;" >
{table}
</p>
<p>Regards,</p>
<p>Me</p>
</body></html>
"""
df = pd.read_csv("MyFile.csv")
col_list = list(df.columns.values)
# here again a tried to setstyle directly on the pandas dataframe.
df.style.set_properties(**{'background-color': 'white',
'color': 'yellow',
'border-color': 'black'})
data = df
text = text.format(table=tabulate(data, headers=col_list, tablefmt="grid"))
html = html.format(table=tabulate(data, headers=col_list, tablefmt="html"))
message = MIMEMultipart(
"alternative", None, [MIMEText(text), MIMEText(html,'html')])
message['Subject'] = "sending early morning "
message['From'] = me
message['To'] = you
import glob, os
os.chdir("D://FolderLogFileUploads//")
file_list = []
filename = "File1.csv"
for file in glob.glob("*.csv"):
file_list.append(file)
if filename in file_list:
target_file = filename
attachment = open(target_file, 'rb')
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= "+filename)
message.attach(part)
server = smtplib.SMTP(server)
server.ehlo()
server.starttls()
server.login(me, password)
server.sendmail(me, you, message.as_string())
print("message with attachment sent successfully")
server.quit()
PS:my col list is ["name", "city", "e_id", "pay_band"] and i want my col: "e_id" and "city" to be of a specific color say yellow.

Is it possible to iterate a dict in python for use in a html email

I am trying to send a email notification, and I would like to send dynamic data(for testing purposes my dictionary is static). How ever when I iterate trough my dictionary only one value shows up in the email. Any help would be greatly appreciated!!!
Here is my email.py
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# me == my email address
# you == recipient's email address
me = "test_email#mydomain.com"
you = "test_email_user#mydomain.com"
msg = MIMEMultipart('alternative')
msg['Subject'] = "Link"
msg['From'] = me
msg['To'] = you
justin = "Justin"
p = {u'Contracts that need signed by: Justin': [[u'/contracts/26203', u'/contracts/26194', u'/contracts/26199', u'/contracts/26173']]}
for a in p.keys():
b = p['Contracts that need signed by: Justin']
for c, d in enumerate(b):
e = d
for f in e:
html = """\
<html>
<head></head>
<body>
<p>Contracts that need signed by """ + justin + """<br>
How are you?<br>
Here is the link you wanted.
</p>
</body>
</html>
"""
part2 = MIMEText(html, 'html')
msg.attach(part2)
s = smtplib.SMTP('localhost')
s.sendmail(me, you, msg.as_string())
s.quit()
Here is the email I get ^
Contracts that need signed by Justin
How are you?
Here is the link you wanted.
Which I want it to look like this
Contracts that need signed by Justin
How are you?
#EACH OF THE LINKS WOULD BE THE NEXT VALUE IN MY DICTONARY.
Here is the link1 you wanted.
Here is the link2 you wanted.
Here is the link3 you wanted.
Here is the link4 you wanted.
You are overwriting the html variable over and over again.
You have to add always 1 line in the for loop like this:
justin = "Justin"
p = {u'Contracts that need signed by: Justin': [[u'/contracts/26203', u'/contracts/26194', u'/contracts/26199', u'/contracts/26173']]}
for a in p.keys():
b = p['Contracts that need signed by: Justin']
for c, d in enumerate(b):
e = d
html = """\
<html>
<head></head>
<body>
<p>Contracts that need signed by """ + justin + """<br>
How are you?<br>
"""
for f in e:
html = html + """Here is the link you wanted. \n"""
html = html + """</p>
</body>
</html>
"""
part2 = MIMEText(html, 'html')
print part2
The problem is you overwrite html variable on each iteration.
You can use string formatting for your problem:
for c, d in enumerate(b):
e = d
html = """\
<html>
<head></head>
<body>
<p>Contracts that need signed by """ + justin + """<br>
How are you?<br>
{0}
</p>
</body>
</html>
"""
s = []
for f in e:
print(f)
s.append('Here is the <a href="http://contract.mydomain.com/{0}>link</a> you wanted.'.format(f))
print(s)
html = html.format('\n'.join(s))
print(html)
output:
<html>
<head></head>
<body>
<p>Contracts that need signed by aaa<br>
How are you?<br>
Here is the <a href="http://contract.mydomain.com//contracts/26203>link</a> you wanted.
Here is the <a href="http://contract.mydomain.com//contracts/26194>link</a> you wanted.
Here is the <a href="http://contract.mydomain.com//contracts/26199>link</a> you wanted.
Here is the <a href="http://contract.mydomain.com//contracts/26173>link</a> you wanted.
</p>
</body>
</html>

Categories

Resources