python3 + Pandas styles + Change alternate row color - python

Hi i am using Pandas and displaying a table.
I there a function to apply alternate row color to make it clear to read.
Using below code I am sending table in mail and it works.
my code:
count = 1000
df = pandas.DataFrame.from_dict(result)
df["Total"] = df.T.sum()
html = """<!DOCTYPE html>
<html>
<body>
<h3> %i</h3>
{table_content}
</body>
</html>
""" % count
# Create message container - the correct MIME type is
# multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = " Report"
msg['From'] = sender
msg['To'] = recipients
part2 = MIMEText(html.df(
table_content=df.to_html(na_rep="0")), 'html')
msg.attach(part2)

You can use CSS, namely the tr:nth-child in combination with df.to_html(classes)
Adopt to your case:
from IPython.display import display, HTML
from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
iris = load_iris()
df = pd.DataFrame(data= np.c_[iris['data'], iris['target']],
columns= iris['feature_names'] + ['target'])
HTML('''
<style>
.df tbody tr:nth-child(even) { background-color: lightblue; }
</style>
''' + df.to_html(classes="df"))
Update: Expanding to a specific example
I slightly rearranged the code to allow adding css, as it was conflicting with {} used by .format. You can add your variables to html_variables dict and use %()s to embed them into html. If your html becomes too complicated I recommend looking at using some template engine to make it more robust. Otherwise the code below should be self-explanatory.
html_template = '''<!DOCTYPE html>
<html>
<head>
<style>.df tbody tr:nth-child(even) {background-color: lightblue;}</style>
</head>
<body>
<h3>%(other_var)s</h3>
%(table_content)s
</body>
</html>
'''
html_vars = {'other_var':'IRIS Dataset','table_content':df.to_html(classes="df")}
html = html_template % html_vars
# Create message container - the correct MIME type is
# multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "Report"
msg['From'] = sender
msg['To'] = recipient
part2 = MIMEText(html, 'html')
msg.attach(part2)

Old question, but I found a solution within pandas framework which I put for future use:
def rower(data):
s = data.index % 2 != 0
s = pd.concat([pd.Series(s)] * data.shape[1], axis=1) #6 or the n of cols u have
z = pd.DataFrame(np.where(s, 'background-color:#f2f2f2', ''),
index=data.index, columns=data.columns)
return z
df.style.apply(rower, axis=None)

Essentially the same as #oleg's answer, but rather than the html blocks you can use df.style.set_table_styles like such:
df.style.set_table_styles([{"selector":"tbody tr:nth-child(even)","props":[("background-color","lightgrey")]}])
I find it useful to use the df.style and have my own dark-theme styler. Feel free to use if it is helpful:
def dfdark(styler):
#styler.background_gradient(cmap='coolwarm')
#styler.color('white')
styler.set_table_styles([
{
"selector":"thead",
"props":[("background-color","grey")]
},
{
"selector":"tbody tr:nth-child(even)",
"props":[("background-color","lightgrey")]
},
{
"selector":"th.row_heading",
"props":[("background-color","grey")]
},
{
"selector":"td",
"props":[("border","white")]
},
])
return styler
#styler.format(color='grey')
df=pd.DataFrame(index=np.arange(10),columns=[1,2],data=np.random.normal(size=[10,2]))
df.style.pipe(dfdark)
which outputs:

Related

How to send prettytable through email using python

I have created a prettytable in python and I have to send the output of prettytable through email
env = "Dev"
cost = 25.3698
line = [env, "${:,.2f}".format(cost)]
totalcostofenv = PrettyTable(['Environment', 'Cost'])
totalcostofenv.add_row(line)
print(totalcostofenv)
Below attached is the output :
Table Output
Can anyone help me to solve this?
This was my question asked and I found an solution , Below displayed is my code:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import os
from prettytable import PrettyTable
env = "Dev"
cost = 25.3698
line = [env, "${:,.2f}".format(cost)]
totalcostofenv = PrettyTable(['Environment', 'Cost'])
totalcostofenv.add_row(line)
print(totalcostofenv)
print(totalcostofenv.get_html_string())
def trigger_email():
my_message = totalcostofenv.get_html_string()
text = "Hi!"
html = """\
<html>
<head>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th, td {
padding: 5px;
text-align: left;
}
</style>
</head>
<body>
<p>Cost Usage of Plantd Environemnts<br>
%s
</p>
</body>
</html>
""" % (my_message)
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')
msg = MIMEMultipart()
from_addr = "from-address"
mail_password = os.environ.get('gmail-pass')
to_addr = "to-address"
msg.attach(part1)
msg.attach(part2)
try:
smtp = smtplib.SMTP('smtp.gmail.com',587)
smtp.starttls()
smtp.login(from_addr , mail_password)
smtp.sendmail(from_addr , to_addr , msg.as_string())
print('Mail sent')
except:
print('Mail not sent')
trigger_email()
You can use MJML templating like this
<mjml>
<mj-head>
<mj-title>Set the title, usually for accessibility tools</mj-title>
<mj-preview>Set inbox preview text here, otherwise it might be something nonsensical</mj-preview>
<mj-attributes>
<mj-all font-family="Helvetica, Arial, sans-serif"></mj-all>
<mj-text font-weight="400" font-size="16px" color="#4A4A4A" line-height="24px" />
<mj-section padding="0px"></mj-section>
</mj-attributes>
</mj-head>
<mj-body>
{{table}}
</mj-body>
</mjml>
Code:
import pystache
# read in the email template, remember to use the compiled HTML version!
email_template = (Path() / 'email_template.html').read_text()
# Logic
env = "Dev"
cost = 25.3698
line = [env, "${:,.2f}".format(cost)]
totalcostofenv = PrettyTable(['Environment', 'Cost'])
totalcostofenv.add_row(line)
# Pass in values for the template using a dictionary
template_params = {'table': totalcostofenv }
# Attach the message to the Multipart Email
final_email_html = pystache.render(email_template, template_params)
message.attach(MIMEText(final_email_html), 'html')
"""Continue with sending..."""

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)

Send pandas dataframe data as html e-mail

I want to send a pandas dataframe data as an HTML e-mail. Based on this post I could create an html with the dataframe. Code
import pandas as pd
import numpy as np
HEADER = '''
<html>
<head>
</head>
<body>
'''
FOOTER = '''
</body>
</html>
'''
df = pd.DataFrame([[1.1, 1.1, 1.1, 2.6, 2.5, 3.4,2.6,2.6,3.4,3.4,2.6,1.1,1.1,3.3], list('AAABBBBABCBDDD')]).T
with open('test.html', 'w') as f:
f.write(HEADER)
f.write(df.to_html(classes='df'))
f.write(FOOTER)
Now I want to send this as a html e-mail. I tried this. Can not figure out how to attach the html file?
Pandas has a function for this.
This will give the the html code for the table, after which you can embed it into an email with:
df = DataFrame(data)
email = " some html {df} lah lah"
email = email.format(df=df.to_html())
Finally found. This is the way it should be done.
filename = "test.html"
f = file(filename)
attachment = MIMEText(f.read(),'html')
msg.attach(attachment)

Categories

Resources