【问题标题】:Add header and text color in HTML table email body based on disk usage condition using Python使用 Python 根据磁盘使用情况在 HTML 表格电子邮件正文中添加标题和文本颜色
【发布时间】:2022-01-13 01:09:56
【问题描述】:

我正在使用 python 在电子邮件正文中发送一个 html 表格。 html 表由磁盘使用情况组成,当磁盘使用率高于 80% 时,我需要在表中添加标题(第一行)和红色文本。

这是我正在使用的代码,用于获取带有文本颜色的电子邮件,但它不包括标题(服务器、总大小、总数据、使用百分比):

me = 'noreply@automationtest.com'

server = 'some smtp server'
you = 'email@someautomation.com'

text = """
{table}
"""

html = """
<html>
<head>
<style> 
  table, th, td {{ border: 1px solid black; border-collapse: collapse; }}
  th, td {{ padding: 5px; }}
</style>
</head>
<body><p style="font-family:verdana">Hi,</p>
<p style="font-family:verdana">sometext</p>
{table}
<p style="font-family:verdana">sometext</p>
<p style="font-family:verdana">Regards</p>
<p style="font-family:verdana">someme</p>
</body></html>
""" 

with open('files/file.csv') as input_file:
    reader = DictReader(input_file)
    data = list(reader)
    for row in data:
      row['Usage in %'] = pd.to_numeric(row['Usage in %'])
      if row['Usage in %'] >= 80:
      row['Usage in %'] = "<p style='color:red'>%s</p>"%row['Usage in %']


text = text.format(table=tabulate(data, headers="firstrow", tablefmt="grid"))

html = html.format(table=tabulate(data, headers="firstrow", tablefmt="unsafehtml"))
message = MIMEMultipart("alternative", None, [MIMEText(text), MIMEText(html,'html')])
print(html)


message['From'] = me
message['To'] = you
server = smtplib.SMTP(server)
server.ehlo()
server.starttls()
server.login(me, password)
server.sendmail(me, you, message.as_string())
server.quit()

我得到以下带有文本颜色但没有标题的输出:

预期输出:

非常感谢任何帮助。

【问题讨论】:

    标签: python html pandas pandas-styles tabulate


    【解决方案1】:

    在pandas 1.3.0 及更新版本中,最合适的方式是使用pandas Table Visualization 并创建一个Subclass

    创建一个文件夹“templates”和两个文件“myhtml.tpl”和“mystyles.tpl”

    在 myhtml.tpl 中添加所需的任何额外 HTML 代码:

    {% extends "html_table.tpl" %}
    {% block table %}
    <p style="font-family:verdana">Hi,</p>
    <p style="font-family:verdana">sometext</p>
    {{ super() }}
    <p style="font-family:verdana">sometext</p>
    <p style="font-family:verdana">Regards</p>
    <p style="font-family:verdana">someme</p>
    {% endblock table %}
    

    在 mystyles.tpl 添加任何其他样式:

    {% extends "html_style.tpl" %}
    {% block style %}
    {{ super() }}
    <style>
        table, th, td {
            border: 1px solid black;
            border-collapse: collapse;
        }
    
        th, td {
            padding: 5px;
        }
    </style>
    {% endblock style %}
    

    (生成此文件结构的代码在此答案的末尾)


    我们现在可以使用from_custom_template 生成Styler 子类

    import numpy as np
    import pandas as pd
    from pandas.io.formats.style import Styler
    
    # Build Styler Subclass from templates
    MyStyler = Styler.from_custom_template(
        "templates",  # Folder to Search
        html_table="myhtml.tpl",  # HTML Template
        html_style='mystyles.tpl'  # CSS Template
    )
    # trim extra whitespace from HTML
    MyStyler.env.trim_blocks = True
    
    # Read in CSV
    df = pd.read_csv('files/file.csv')
    
    # Add styles using Styler apply and render to_html
    html = MyStyler(df).apply(
        lambda s: np.where(s >= 80, 'color: red', None), subset='Usage in %'
    ).format(
        # Apply format string (remove insignificant zeros)
        formatter='{:g}', subset='Usage in %'
    ).hide_index().to_html(doctype_html=True)
    
    print(html)
    

    生成的 html 字符串类似于:

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="utf-8">
    
      <style type="text/css">
        #T_22fdb_row2_col3,
        #T_22fdb_row3_col3 {
          color: red;
        }
      </style>
    
      <style>
        table,
        th,
        td {
          border: 1px solid black;
          border-collapse: collapse;
        }
        
        th,
        td {
          padding: 5px;
        }
      </style>
    </head>
    
    <body>
    
      <p style="font-family:verdana">Hi,</p>
      <p style="font-family:verdana">sometext</p>
      <table id="T_22fdb_">
        <thead>
          <tr>
            <th class="col_heading level0 col0">Server</th>
            <th class="col_heading level0 col1">Total size</th>
            <th class="col_heading level0 col2">Total Data in</th>
            <th class="col_heading level0 col3">Usage in %</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td id="T_22fdb_row0_col0" class="data row0 col0">A</td>
            <td id="T_22fdb_row0_col1" class="data row0 col1">100</td>
            <td id="T_22fdb_row0_col2" class="data row0 col2">25</td>
            <td id="T_22fdb_row0_col3" class="data row0 col3">25</td>
          </tr>
          <tr>
            <td id="T_22fdb_row1_col0" class="data row1 col0">B</td>
            <td id="T_22fdb_row1_col1" class="data row1 col1">100</td>
            <td id="T_22fdb_row1_col2" class="data row1 col2">20</td>
            <td id="T_22fdb_row1_col3" class="data row1 col3">20</td>
          </tr>
          <tr>
            <td id="T_22fdb_row2_col0" class="data row2 col0">C</td>
            <td id="T_22fdb_row2_col1" class="data row2 col1">100</td>
            <td id="T_22fdb_row2_col2" class="data row2 col2">85</td>
            <td id="T_22fdb_row2_col3" class="data row2 col3">85.6</td>
          </tr>
          <tr>
            <td id="T_22fdb_row3_col0" class="data row3 col0">D</td>
            <td id="T_22fdb_row3_col1" class="data row3 col1">100</td>
            <td id="T_22fdb_row3_col2" class="data row3 col2">90</td>
            <td id="T_22fdb_row3_col3" class="data row3 col3">90.8</td>
          </tr>
        </tbody>
      </table>
    
      <p style="font-family:verdana">sometext</p>
      <p style="font-family:verdana">Regards</p>
      <p style="font-family:verdana">someme</p>
    </body>
    
    </html>

    解决这个问题的更简单的方法是直接使用格式化操作 html 字符串,但是,这可能会导致 html 格式不正确:

    import numpy as np
    import pandas as pd
    
    html = """
    <html>
    <head>
    <style> 
      table, th, td {{ border: 1px solid black; border-collapse: collapse; }}
      th, td {{ padding: 5px; }}
    </style>
    </head>
    <body><p style="font-family:verdana">Hi,</p>
    <p style="font-family:verdana">sometext</p>
    {table}
    <p style="font-family:verdana">sometext</p>
    <p style="font-family:verdana">Regards</p>
    <p style="font-family:verdana">someme</p>
    </body></html>
    """
    
    # Read in CSV
    df = pd.read_csv('files/file.csv')
    
    # Add styles using Styler apply and render to_html
    table_html = df.style.apply(
        lambda s: np.where(s >= 80, 'color: red', None), subset='Usage in %'
    ).format(
        # Apply format string (remove insignificant zeros)
        formatter='{:g}', subset=['Total Data in', 'Usage in %']
    ).hide_index().to_html()
    
    html = html.format(table=table_html)
    
    print(html)
    

    这会导致以下 html(注意样式元素放错位置):

    <html>
    <head>
    <style> 
      table, th, td { border: 1px solid black; border-collapse: collapse; }
      th, td { padding: 5px; }
    </style>
    </head>
    <body><p style="font-family:verdana">Hi,</p>
    <p style="font-family:verdana">sometext</p>
    <style type="text/css">
    #T_139a3_row2_col3, #T_139a3_row3_col3 {
      color: red;
    }
    </style>
    <table id="T_139a3_">
      <thead>
        <tr>
          <th class="col_heading level0 col0" >Server</th>
          <th class="col_heading level0 col1" >Total size</th>
          <th class="col_heading level0 col2" >Total Data in</th>
          <th class="col_heading level0 col3" >Usage in %</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td id="T_139a3_row0_col0" class="data row0 col0" >A</td>
          <td id="T_139a3_row0_col1" class="data row0 col1" >100</td>
          <td id="T_139a3_row0_col2" class="data row0 col2" >25</td>
          <td id="T_139a3_row0_col3" class="data row0 col3" >25</td>
        </tr>
        <tr>
          <td id="T_139a3_row1_col0" class="data row1 col0" >B</td>
          <td id="T_139a3_row1_col1" class="data row1 col1" >100</td>
          <td id="T_139a3_row1_col2" class="data row1 col2" >20</td>
          <td id="T_139a3_row1_col3" class="data row1 col3" >20</td>
        </tr>
        <tr>
          <td id="T_139a3_row2_col0" class="data row2 col0" >C</td>
          <td id="T_139a3_row2_col1" class="data row2 col1" >100</td>
          <td id="T_139a3_row2_col2" class="data row2 col2" >85</td>
          <td id="T_139a3_row2_col3" class="data row2 col3" >85.6</td>
        </tr>
        <tr>
          <td id="T_139a3_row3_col0" class="data row3 col0" >D</td>
          <td id="T_139a3_row3_col1" class="data row3 col1" >100</td>
          <td id="T_139a3_row3_col2" class="data row3 col2" >90</td>
          <td id="T_139a3_row3_col3" class="data row3 col3" >90.8</td>
        </tr>
      </tbody>
    </table>
    
    <p style="font-family:verdana">sometext</p>
    <p style="font-family:verdana">Regards</p>
    <p style="font-family:verdana">someme</p>
    </body></html>

    构建模板文件夹和两个模板文件的简单脚本:

    import pathlib
    
    
    # Code to generate the Templates and folder
    pathlib.Path('./templates').mkdir(exist_ok=True)
    with open('./templates/myhtml.tpl', 'w') as f:
        f.write('''
    {% extends "html_table.tpl" %}
    {% block table %}
    <p style="font-family:verdana">Hi,</p>
    <p style="font-family:verdana">sometext</p>
    {{ super() }}
    <p style="font-family:verdana">sometext</p>
    <p style="font-family:verdana">Regards</p>
    <p style="font-family:verdana">someme</p>
    {% endblock table %}
    '''.strip())
    with open('./templates/mystyles.tpl', 'w') as f:
        f.write('''
    {% extends "html_style.tpl" %}
    {% block style %}
    {{ super() }}
    <style>
        table, th, td {
            border: 1px solid black;
            border-collapse: collapse;
        }
    
        th, td {
            padding: 5px;
        }
    </style>
    {% endblock style %}
    '''.strip())
    

    files/file.csv 内容:

    Server,Total size,Total Data in,Usage in %
    A,100,25,25
    B,100,20,20
    C,100,85,85.6
    D,100,90,90.8
    

    【讨论】:

    • 嗨@Henry,非常感谢您的回复。这很棒并且按预期工作。但是,当我在 csv 文件中添加小数时(例如:85.5 和 90.7),我得到了额外的零,我进入了 html 85.500000 和 90.700000。如果我想排除零并将值保留为 csv 文件,我不确定如何实现。
    • 默认 Styler 精度为小数点后 6 位 @chiko 我已更新我的答案以包含 format 字符串以删除无关紧要的 0。您可以通过将subsetsubset='Usage in %' 更改为列表subset=['Total Data in', 'Usage in %'] 来包含多个列(例如)。
    猜你喜欢
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-11
    • 2014-04-24
    • 1970-01-01
    相关资源
    最近更新 更多