Output the report as PDF from DB with Python and automatically attach it to an email and send it

Part-san checks the operating status of communication equipment on a dedicated screen every day, creates a report, and sends it by e-mail. I think that it is quite common for any company to do similar work, but from an engineer's point of view, such a routine is impossible, so I wrote a little tool.

Way of thinking

We know that all the underlying information is in the database. Therefore, you can implement a program ** that attaches a PDF file generated from the database to an email and sends it ** on the server side, and register it in cron as well.

Execution environment

How to generate a form

I thought about outputting to an Excel file with Python's ** OpenPyXL ** module, [Create resume format PDF with python + reportlab] Inspired by this article, I decided to output a PDF file with the ** ReportLab ** module.

Installation


pip3 install reportlab

Form sample

report.png

Code sample

There are various ways to connect from Python to MySQL, but I happened to have ** MySQLdb ** on the server, so I used it. I will post it without much modification, but I think the scheme is reasonably versatile, so please refer to it.

Python


"""
Generate a report in PDF format from the database and send it as an attachment to an email
"""
__author__  = "MindWood"
__version__ = "1.00"
__date__    = "31 Oct 2019"

import MySQLdb
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4, portrait
from reportlab.lib.units import mm
from reportlab.platypus import Table, TableStyle
import smtplib
from email import encoders
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
import os
import datetime

def setup_page():
    """Register the font and set the header and footer"""
    pdfmetrics.registerFont(UnicodeCIDFont(font_name))  #Font registration
    #Header drawing
    c.setFont(font_name, 18)
    c.drawString(10*mm, height - 15*mm, "Communication equipment operation status report")
    c.setFont(font_name, 9)
    c.drawString(width - 58*mm, height - 10*mm, header_date)
    #Footer drawing
    c.drawString(10*mm, 16*mm, "The terminal ID that has received data within the last 8 hours is displayed in green.")
    global page_count
    c.drawString(width - 15*mm, 5*mm, "{}page".format(page_count))  #Draw page number
    page_count += 1

def control_break():
    """Control break with customer name"""
    if ctrl_break_key == "": return
    c.showPage()
    setup_page()

def page_break(n):
    """Page break processing"""
    n += 1
    if n < 28: return n
    c.showPage()
    setup_page()
    return 0

#Setting the base date and time
dt = datetime.datetime.now()
header_date = "{:%Y year%-m month%-d day%-H o'clock%-M minutes now}".format(dt)
safe_date = "{:%Y/%m/%d %H:%M}".format(dt + datetime.timedelta(hours=-8))  #8 hours ago

#PDF file initialization
pdf_filename = "report{:%y%m}.pdf".format(dt)
c = canvas.Canvas(pdf_filename, pagesize=portrait(A4))  #PDF file name and paper size
width, height = A4  #Get paper size
c.setAuthor("MindWood")
c.setTitle("IoT gateway Working report")
c.setSubject("")

font_name = "HeiseiKakuGo-W5"  #Font name
page_count = customer_no = 1
setup_page()
ctrl_break_key = ""

#Connect to MySQL
conn = MySQLdb.connect(host="localhost", user="xxxxxx", passwd="yyyyyy", db="zzzzzz", charset="utf8")
cursor = conn.cursor(MySQLdb.cursors.DictCursor)

#Acquisition of master information of communication equipment
cursor.execute('''
SELECT c.Name as CustName, d.Name as DeptName, a.Code, a.Area, hex(a.MacAddress) as MacAddress
FROM table0001 a
LEFT JOIN table0002 c ON a.CustomerID = c.CustomerID
LEFT JOIN table0003 d ON a.CustomerID = d.CustomerID AND a.DeptID = d.DeptID
ORDER BY c.CustomerID, d.DeptID, MacAddress;
''')

gws = cursor.fetchall()
for row_gw in gws:
    #Page break when customer name changes
    if ctrl_break_key != row_gw["CustName"]:
        control_break()
        ctrl_break_key = row_gw["CustName"]
        c.setFont(font_name, 15)
        c.drawString(10*mm, height - 36*mm, "{}. {}".format(customer_no, ctrl_break_key))
        customer_no += 1

        data = [ [ "Department name", "Management code", "Installation area", "MAC address" ] ]  #Communication equipment heading
        table = Table(data, colWidths=(70*mm, 40*mm, 40*mm, 40*mm), rowHeights=8*mm)  #Creating a table
        table.setStyle(TableStyle([
            ("FONT", (0, 0), (-1, -1), font_name, 11),            #font
            ("BOX", (0, 0), (-1, -1), 1, colors.black),           #Outer ruled line
            ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black),  #Inner ruled line
            ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),               #Align characters vertically in the center
            ("BACKGROUND", (0, 0), (-1, -1), colors.lightgrey),   #Fill with gray
        ]))
        table.wrapOn(c, 10*mm, height - 50*mm)  #Table position
        table.drawOn(c, 10*mm, height - 50*mm)  #Table position
        line_count = 1

    styles = [
        ("FONT", (0, 0), (-1, -1), font_name, 11),
        ("BOX", (0, 0), (-1, -1), 1, colors.black),
        ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black),
        ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
    ]
    MacAddress = row_gw["MacAddress"]
    if os.uname()[1] == "ip-172-35-10-XX":  #For a specific server
        MacAddress = "XXXXXXXXXXXX"
        styles.append(("BACKGROUND", (3, 0), (3, 0), colors.yellow))  #Fill with yellow

    data = [ [ row_gw["DeptName"], row_gw["Code"], row_gw["Area"], MacAddress ] ]  #Detailed data of communication equipment
    table = Table(data, colWidths=(70*mm, 40*mm, 40*mm, 40*mm), rowHeights=8*mm)
    table.setStyle(TableStyle(styles))
    table.wrapOn(c, 10*mm, height - 50*mm - 8*mm * line_count)
    table.drawOn(c, 10*mm, height - 50*mm - 8*mm * line_count)
    line_count = page_break(line_count)

    #Acquisition of data reception date and time
    cursor.execute('''
    SELECT hex(TermID) as TermID, from_unixtime(min(Time),"%Y/%m/%d %H:%i:%S") as from_date, from_unixtime(max(Time),"%Y/%m/%d %H:%i:%S") as to_date FROM table0005
    WHERE MacAddress=0x{} GROUP BY TermID ORDER BY to_date;
    '''.format(MacAddress))

    terms = cursor.fetchall()
    for row_term in terms:
        data = [ [ "period", row_term["from_date"] + " ~ " + row_term["to_date"], "Terminal ID", row_term["TermID"] ] ]  #Detailed data of the terminal
        table = Table(data, colWidths=(25*mm, 100*mm, 25*mm, 40*mm), rowHeights=8*mm)
        styles = [
            ("FONT", (0, 0), (-1, -1), font_name, 11),
            ("BOX", (0, 0), (-1, -1), 1, colors.black),
            ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black),
            ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
            ("BACKGROUND", (0, 0), (0, 0), colors.lightgrey),
            ("BACKGROUND", (2, 0), (2, 0), colors.lightgrey),
        ]
        if row_term["to_date"] > safe_date:  #If the last reception date and time is within the last 8 hours
            styles.append(("BACKGROUND", (3, 0), (3, 0), colors.palegreen))
        table.setStyle(TableStyle(styles))
        table.wrapOn(c, 10*mm, height - 50*mm - 8*mm*line_count)
        table.drawOn(c, 10*mm, height - 50*mm - 8*mm*line_count)
        line_count = page_break(line_count)

#MySQL disconnect
cursor.close()
conn.close()

#Save to PDF file
c.showPage()
c.save()

#Send PDF file by email
from_addr = "[email protected]"
to_addr   = "[email protected]"
bcc_addr  = "[email protected]"  #Multiple can be specified separated by commas
rcpt = bcc_addr.split(",") + [to_addr]

msg = MIMEMultipart()
msg["Subject"] = "Communication equipment operation status report{:%Y year%-m month}".format(dt)
msg["From"] = from_addr
msg["To"] = to_addr

msg.attach(MIMEText("""
This email is automatically sent from the system.
The system operation status of the XXXX server is attached.
""".strip()))

attachment = MIMEBase("application", "pdf")
file = open(pdf_filename, "rb+")
attachment.set_payload(file.read())
file.close()
encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition", "attachment", filename=pdf_filename)
msg.attach(attachment)

smtp = smtplib.SMTP("smtp.xxxxxx.com", 587)  #SMTP server
smtp.starttls()
smtp.login(from_addr, "PASSWORD")  #password
smtp.sendmail(from_addr, rcpt, msg.as_string())
smtp.close()

Recommended Posts

Output the report as PDF from DB with Python and automatically attach it to an email and send it
To automatically send an email with an attachment using the Gmail API in Python
I tried to make it possible to automatically send an email just by double-clicking the [Python] icon
[python] Send the image captured from the webcam to the server and save it
Send an email to Spushi's address with python
I tried to make it possible to automatically send an email just by double-clicking the [GAS / Python] icon
Extract images and tables from pdf with python to reduce the burden of reporting
It is easy to execute SQL with Python and output the result in Excel
[Python] Send an email from gmail with two-step verification set
Precautions when inputting from CSV with Python and outputting to json to make it an exe
[Python] Send an email with outlook
HTML email with image to send with python
Send an email with Amazon SES + Python
What to do if you couldn't send an email to Yahoo with Python.
Send an email to a specific email address with python without SMTP settings
Extract the TOP command result with USER and output it as CSV
Send and receive image data as JSON over the network with Python
[Outlook] I tried to automatically create a daily report email with Python
Read CSV file with Python and convert it to DataFrame as it is
I ran GhostScript with python, split the PDF into pages, and converted it to a JPEG image.
The story of making a tool to load an image with Python ⇒ save it as another name
Read the data of the NFC reader connected to Raspberry Pi 3 with Python and send it to openFrameworks with OSC
How to input a character string in Python and output it as it is or in the opposite direction.
Send an email from the VirtualBox CentOS 8 server using your Google account as the sending address and using the app password
Read JSON with Python and output as CSV
[Python] Concatenate a List containing numbers and write it to an output file.
[Free] Hit the Clash Royale API from lambda and send it to LINE
Send an email with Excel attached in Python
I tried to automatically send the literature of the new coronavirus to LINE with Python
I managed to do it because the custom of attaching a zip with a password to an email and saying "I will send you the password separately" is troublesome.
If you can't send an email with python smtplib and have trouble, command line
Pass an array from PHP to PYTHON and do numpy processing to get the result
Procedure to load MNIST with python and output to png
Try to operate DB with Python and visualize with d3
Convert the image in .zip to PDF with Python
Get mail from Gmail and label it with Python3
Read json file with Python, format it, and output json
Specify MinGW as the compiler to use with Python
Try to automate pdf format report creation with Python
Send email with Python
I want to cut out only the face from a person image with Python and save it ~ Face detection and trimming with face_recognition ~
Try to generate a cyclic peptide from an amino acid sequence with Python and RDKit
Recursively get the Excel list in a specific folder with python and write it to Excel.
I made a server with Python socket and ssl and tried to access it from a browser
Return the image data with Flask of Python and draw it to the canvas element of HTML
I want to copy an English paper from pdf and put it in Google Translate
[Python / Ruby] Understanding with code How to get data from online and write it to CSV
An easy way to view the time taken in Python and a smarter way to improve it
Put Ubuntu in Raspi, put Docker on it, and control GPIO with python from the container
Probably the easiest way to create a pdf with Python3
[pyqtgraph] Add region to the graph and link it with the graph region
Sort and output the elements in the list as elements and multiples in Python.
Send experiment results (text and images) to slack with Python
[Python] I installed the game from pip and played it
Code to send an email based on the Excel email list
How to log in to AtCoder with Python and submit automatically
(Memo) Until you extract only the part you want from a certain Web page, convert it to a Sphinx page, and print it as a PDF
Send Japanese email with Python3
I want to extract an arbitrary URL from the character string of the html source with python
[Python] What is pip? Explain the command list and how to use it with actual examples
[Python] How to scrape a local html file and output it as CSV using Beautiful Soup