Story: This series of articles assume you are an employee of the City of Sacramento’s IT Department.
At the end of each month, a CSV file is sent to the Chief of Police outlining the crime statistics for the current month.
However, the Chief prefers to view the output in a styled PDF format based on a District/Beat of his choosing.
Part 3 of this series is a continuation of Part 1 – Part 2 and focuses on:
- creating an email.
- adding in the following to the email:
- the sender’s email address
- the receiver’s email address
- the email subject
- the email body
- attaching the PDF created in Part 2
- sending the email with the attachment.
Preparation
This article assumes you have completed the following from Part 1:
import pandas as pd from fpdf import FPDF import csv import datetime import yagmail
Update Gmail Settings
In this article, we create an email. This email contains a subject, body message, and a PDF file.
π‘ Note: To email from a Corporate Account, omit these instructions.
Gmail has settings that prevent sending emails via code. To circumvent this issue:
- Navigate to and log in to your Gmail account.
- On the top-right icon, click to select
Manage your Google Account. - On the left-hand side of the
Manage your Google Accountpage, clickSecurity. - On the right-hand side of the
Securitypage, navigate to2-step verification. - On the
Less secure app accesspage, turn off this option.
After completing these steps, you are now ready to send an email with an attachment.
π‘ Note: After running this code, turn the 2-step verification back on.
Setup Email Variables
This section declares variables for sending the email.
fname = f'{rpt_num}.pdf'
esender = 'youremail@gmail.com'
ereceiver = 'chiefpolice@cityofsacramento.com'
esubject = 'Crime Stats Report'
ebody = '''
<p>Please find attached the Crime Stats PDF file as requested.</p>
<p>Regards,</p>
<p>Matt Bond</p>
'''- Line [1] creates the
fnamevariable containing therpt_numselected in Part 1. The extension ‘.pdf’ is appended to the variable. - Line [2] creates the
esendervariable containing your email address. - Line [3] creates the
ereceivervariable containing the receiver’s email address. - Line [4] creates the
esubjectvariable containing the email subject. - Line [5] creates the
ebodyvariable containing the body of the email.
Create & Send the Email
This section formulates the email, attaches the PDF, and sends it.
def send_yagmail(ereceiver, esubject, ebody, fname):
yag = yagmail.SMTP(esender, 'yourgmailpassword')
yag.send(to=ereceiver, subject=esubject, contents=ebody, attachments=fname)
send_yagmail(ereceiver, esubject, ebody, fname)
print(f'The email was sent to {esender}.')Validate the Code
The email created above is sent to the Gmail account, ereceiver.
After navigating to and opening the Gmail account, everything works as expected.
Output

Great job!
Finishing Up
Below is the complete code from Part 1, Part 2, and this article, Part 3.
import pandas as pd
from fpdf import FPDF
import csv
import datetime
import yagmail
cols = ['cdatetime', 'address', 'district', 'beat', 'grid', 'crimedescr']
df = pd.read_csv('crimes.csv', usecols=cols)
df.sort_values('cdatetime', inplace=True, ascending=True)
df['beat'] = df['beat'].str.rstrip()
df = df.apply(lambda x: x.astype(str).str.title())
lst = '123456ABCQ'
rpt_num = None
while True:
rpt_num = input('Select a District/Beat (1A-6C or Q to quit): ').upper()
if rpt_num == 'Q':
exit()
elif rpt_num[0] not in lst[0:6] or rpt_num[1] not in lst[6:9]:
print('You entered an invalid selection!')
else:
break
print(f'Report {rpt_num} generating!')
the_filter = (df.query(f"beat == '{rpt_num}'"))
filt_cols=['cdatetime','address','grid','crimedescr']
the_filter.to_csv(f'{rpt_num}.csv', columns=filt_cols)
print(f'Report {rpt_num}.csv resides in the current working directory!')
with open(f'{rpt_num}.csv', 'r') as csvfile:
data_list = list(csv.reader(csvfile))[1:]
pdf_name = f'{rpt_num}.pdf'
rpt_hdgs = ['Row #', 'Date/Time', 'Address', 'Grid', 'Description']
cwidths = [20, 40, 50, 30, 55]
rpt_font_sz = 7
hdg_font_sz = 11
line_height = 6
class PDF(FPDF):
def header(self):
today = datetime.date.today()
date_fmt = today.strftime("%B" " " "%d" ", " "%Y")
self.l_margin = 6
self.r_margin = 6
self.set_font('Arial', '', rpt_font_sz)
self.image('sacramento_logo.png', 10, 8, 36)
self.cell(80)
self.set_font('Arial', '', hdg_font_sz)
self.set_text_color(43,60,102)
self.cell(30, 3, f'District/Beat: {rpt_num}', 0, 0, 'C')
self.set_font('Arial', '', rpt_font_sz)
self.cell(-30, 11, f'{date_fmt}', 0, 0, 'C')
self.ln(12)
self.set_fill_color(240,248,255)
col = 0
while col < len(rpt_hdgs):
col_width = cwidths[col]
self.cell(col_width, line_height, rpt_hdgs[col], 0, 0, fill=True)
col += 1
self.ln(12)
def footer(self):
# self.set_y(-15)
self.set_font('Arial', 'I', rpt_font_sz)
self.set_fill_color(240,248,255)
self.cell(0, line_height, 'Report Page ' + str(self.page_no()) + '/{nb}', 0, 0, 'C', fill=True)
def convert_to_pdf(data_list):
pdf = PDF()
pdf.alias_nb_pages()
pdf.add_page()
pdf.set_font('Arial', '', rpt_font_sz)
row_count = 0
while row_count < len(data_list):
col = 0
for c in cwidths:
pdf.cell(c, 0, data_list[row_count][col], align='L', border=0)
col += 1
pdf.ln(4)
row_count += 1
pdf.output(pdf_name, 'F')
convert_to_pdf(data_list)
fname = f'{rpt_num}.pdf'
esender = 'asender.coder@gmail.com'
ereceiver = 'areceiver@gmail.com'
esubject = 'Crime Stats Report'
ebody = '''
<p>Please find attached the Crime Stats PDF file as requested.</p>
<p>Regards,<br/>
Matt Brody</p>
'''
def send_yagmail(ereceiver, esubject, ebody, fname):
yag = yagmail.SMTP(esender, 'F*YTax&obkK^&VuS!!!@')
yag.send(to=ereceiver, subject=esubject, contents=ebody, attachments=fname)
send_yagmail(ereceiver, esubject, ebody, fname)
print(f'The email has been sent to {esender}.')Summary
In this article, you learned how to:
- Create an email
- Set the sender, receiver, subject of the email
- Attach a PDF to the email
- Send the email
π§© Challenge: The Finxter Challenge is to write additional code to cc the email to another user.