Python自定义模块:转换生成HTML表格

由于自动提醒邮件中需要发送表格,并且需要根据提醒事项到截止日期的剩余天数自动调整颜色,每次直接写起来比较麻烦,因此自定义了一个模块html_self来实现此功能,需要用到的时候导入此模块即可。

#将表格转换为html
import copy

def get_sort_list(l_type):
    if l_type == '年级':
        return ['小学', '初中', '高中']
    if l_type == '学科':
        return ['语文', '数学', '英语', '物理', '化学']

def sort_data(data, sortlist):
    def get_sort_num(sort_dict, item):
        for k in sort_dict:
            if k in item:
                return sort_dict[k]
        return len(sort_dict)

    for i in range(len(sortlist) - 1, -1, -1):
        sort_mode = sortlist[i]
        sort_dict = {}
        sort_len = len(sort_mode)
        for j in range(sort_len):
            sort_dict[sort_mode[j]] = j
        data = sorted(data, key=lambda x: get_sort_num(sort_dict, x[i]))
    return data


def format_data(data_c, rowspan=1, colspan=1, color='', size='', face=''):
    #data = [i.copy() for i in data_c]
    data = copy.deepcopy(data_c)
    for i in range(len(data)):
        for j in range(len(data[i])):
            data[i][j] = [data[i][j], rowspan, colspan, color, size, face]
    return data


def rowspan_data(data, datacol):
    datacol = min(len(data), datacol)
    last_split = [0, len(data)]  # 前一列的分片,后一列分片要在前一列分片的基础上再分
    for j in range(datacol):  # 遍历列
        next_split = [0]
        for spliti in range(len(last_split) - 1):  # 遍历分片
            item_num = {}  # 值和出现次数
            item_list = []  # 值出现顺序,去重
            for i in range(last_split[spliti], last_split[spliti + 1]):  # 遍历分片下的单元格
                item = data[i][j][0]  # 单元格的值
                if item not in item_num:
                    item_list.append(item)
                    item_num[item] = 0
                item_num[item] = item_num[item] + 1  # 统计出现次数
            item_in = set()  # 合并单元格rowspan除了第一个外都写0
            for i in range(last_split[spliti], last_split[spliti + 1]):  # 遍历分片下的单元格
                item = data[i][j][0]  # 单元格的值
                if item not in item_in:
                    data[i][j][1] = item_num[item]  # 把出现次数写进去
                    item_in.add(item)
                else:
                    data[i][j][1] = 0

            num_list = []  # 分片内值和次数
            for item in item_list:
                num_list.append(item_num[item])
            last_num = next_split[-1]
            for num in num_list:
                next_split.append(last_num + num)
                last_num = next_split[-1]
        last_split = next_split  # 生成新的分片
    return data


def colspan_data(data, datacol):
    datacol = min(len(data[0]), datacol)
    for i in range(len(data)):
        item_num = {}  # 值和出现次数
        item_list = []  # 值出现顺序,去重
        for j in range(datacol):
            item = data[i][j][0]
            if item not in item_num:
                item_list.append(item)
                item_num[item] = 0
            item_num[item] = item_num[item] + 1  # 统计出现次数
        item_in = set()  # 合并单元格rowspan除了第一个外都写0
        for j in range(datacol):
            item = data[i][j][0]  # 单元格的值
            if item not in item_in:
                data[i][j][2] = item_num[item]  # 把出现次数写进去
                item_in.add(item)
            else:
                data[i][j][2] = 0
    return data


def font_data(data, args):
    #根据指定列的值修改颜色
    color2 = args[3] if len(args) > 3 else ''
    color3 = args[5] if len(args) > 5 else ''
    column_color2 = args[4] if len(args) > 4 else -1
#    if column_color2 >= 0: data[i][j][3] = color2 for j in range(len(data[i])) for i in range(len(data)) if data[i][column_color2][0] <= 0
    for i in range(len(data)):
        for j in range(len(data[i])):
            if column_color2 >= 0 and data[i][column_color2][0] < 0:
                data[i][j][3] = color2
            if column_color2 >= 0 and data[i][column_color2][0] == 0 and len(args) > 5:
                data[i][j][3] = color3
    return data


def get_sub_html(h_type, args=[]):
    colorcode = {'gray': '#ECEDF2;'}
    if h_type == 'table0':
        html = "<br><table border='1' cellspacing='0' cellpadding='0' style=\" border: 1px solid #CECFD4; border-collapse: collapse; font-size: 12px; " + \
               "font-family: 'Helvetica Neue',Helvetica,'PingFang SC','Hiragino Sans GB','Microsoft YaHei','微软雅黑',Arial,sans-serif;\">\n"
    elif h_type == 'table1':
        html = "</table><br>\n"

    elif h_type == 'tr0':
        back_color = args[0] if len(args) > 0 else ''
        back_color = colorcode.get(str(back_color), '')
        html = '<tr style="text-align: center; vertical-align: middle; height: 30px; background-color: %s">\n' % (
            back_color)
    elif h_type == 'tr1':
        html = '</tr>\n'

    elif h_type == 'th0':
        rowspan = args[0] if len(args) > 0 else 1
        colspan = args[1] if len(args) > 1 else 1
        width = args[2] if len(args) > 2 else 80
        width = 80 if width == 0 else width
        html = '<th colspan="%s" rowspan="%s" style="border: 1px solid #CECFD4; width:%spx;" align="center">' % (
            colspan, rowspan, width)
    elif h_type == 'th1':
        html = '</th>\n'

    elif h_type == 'td0':
        rowspan = args[0] if len(args) > 0 else 1
        colspan = args[1] if len(args) > 1 else 1
        width = args[2] if len(args) > 2 else 80
        width = 80 if width == 0 else width
        align = args[3] if len(args) > 3 else 'center'
        html = '<td colspan="%s" rowspan="%s" style="border: 1px solid #CECFD4; width:%spx;" align="%s">' % (
            colspan, rowspan, width, align)
    elif h_type == 'td1':
        html = '</td>\n'

    elif h_type == 'font0':
        color = args[0] if len(args) > 0 else ''
        size = args[1] if len(args) > 1 else ''
        face = args[2] if len(args) > 2 else ''
        html = '<font color="%s" size="%s" face="%s">' % (color, size, face)
    elif h_type == 'font1':
        html = '</font>\n'

    else:
        html = ''
    return html


def get_html_data(data, titles, head, widths=[], aligns=[]):
    html = get_sub_html('table0')
    if head is not None:
        head, face, size, color = head
        html += get_sub_html('tr0', ['gray']) + \
                get_sub_html('th0', [1, len(titles[0])]) + get_sub_html('font0', [color, size, face]) + str(head) + get_sub_html('font1') + get_sub_html('th1') + \
                get_sub_html('tr1')

    for row in titles:
        html += get_sub_html('tr0', ['gray'])
        for i in range(len(row)):
            cell, rowspan, colspan, color, size, face = row[i]
            width = widths[i] if len(widths) > i else 80
            if rowspan > 0 and colspan > 0:
                html += get_sub_html('th0', [rowspan, colspan, width]) + get_sub_html('font0', [color, size, face]) + str(
                    cell) + get_sub_html('font1') + get_sub_html('th1')

        #                 html += get_sub_html('th0', [rowspan, colspan, width]) + str(cell) + get_sub_html('th1')
        html += get_sub_html('tr1')

    data_colors = ['None', 'None'] #如果想奇偶行颜色不一样,在此设置
    color_i = 0
    for row in data:
        if row[0][1] > 0 and row[0][2] > 0:  # 换一种颜色
            color_i += 1
        this_color = data_colors[color_i % len(data_colors)]
        html += get_sub_html('tr0', [this_color])
        for i in range(len(row)):
            cell, rowspan, colspan, color, size, face = row[i]
            width = widths[i] if len(widths) > i else 80
            align = aligns[i] if len(aligns) > i else 'center'
            if rowspan > 0 and colspan > 0:
                html += get_sub_html('td0', [rowspan, colspan, width, align]) + get_sub_html('font0', [color, size, face]) + str(
                    cell) + get_sub_html('font1') + get_sub_html('td1')

        #                 html += get_sub_html('td0', [rowspan, colspan, width]) + str(cell) + get_sub_html('td1')
        html += get_sub_html('tr1')

    html += get_sub_html('table1')
    return html


def data_to_html(data, titles, head=None, datacol=0, rowspan=False, colspan=False, widths=[], aligns=[], font=False, font_title=False, font_head=False):
    '''
    data:数据
        type:二维数组
    titles:表头
        type:二维数组
        如果只有一行表头,可以写成[['表头1','表头2']]
    head:表格标题
        type:string
        默认为None,没有标题。
    datacol:数据起始列
        type:int
        数据列不会排序&合并,例如datacol=3,则除了前三列往后都是数据,合并时也不会进行合并
    rowspan:是否垂直合并
        value:True or False
        合并前请先排序,调用sort_data方法即可
    colspan:是否水平合并
        value:True or False
    widths:每列宽度
        type:一维数组
        数组的每个值分别代表了每一列的宽度,默认80,写0也是80
    aligns:每列对齐方式
        type:一维数组
        left,right,center,默认是center
    font:是否需要自定义字体
        value:True or False or ['字体','字号','颜色1','颜色2',特殊颜色的列数(从0开始计算,该列<0则显示颜色2,该列=0则显示颜色3),'颜色3']
        为True时表示,需要根据需求改变字号和颜色,需要修改font_data方法
    font_title:是否需要自定义表头字体
        value:False or ['字体','字号','颜色']
        为True时表示,需要根据需求改变表头字号和颜色
    font_head:是否需要自定义表头字体
        value:False or ['字体','字号','颜色']
        为True时表示,需要根据需求改变标题字号和颜色
    '''
    if head:
        if font_head:
            head = [head]+(font_head + ['',''])[:3]
        else:
            head = [head,'','','']
    if font_title:
        face, size, color = (font_title + ['',''])[:3]
        titles = format_data(titles, size=size, face=face, color=color)
    else:
        titles = format_data(titles)
    if font:
        face, size, color = (font + ['',''])[:3]
        data = format_data(data, size=size, face=face, color=color)
        data = font_data(data, font)
    else:
        data = format_data(data)
    if rowspan == True and datacol > 0:  # 垂直合并
        titles = rowspan_data(titles, len(titles[0]))
        data = rowspan_data(data, datacol)
    if colspan == True and datacol > 0:  # 水平合并
        titles = colspan_data(titles, len(titles[0]))
        data = colspan_data(data, datacol)
    html = get_html_data(data, titles, head, widths, aligns)
    return html

def get_example_data():
    head = '表格标题'
    titles = [['大表头1', '大表头1', '大表头2', '大表头3', '大表头3'],
              ['小表头1', '小表头2', '小表头3', '小表头2', '小表头5']]
    data = [
        ['小学', '语文', 1, 1, 3],
        ['小学', '数学', 1, 5, 1],
        ['小学', '语文', 1, 1, 33],
        ['初中', '数学', 13, 1, 15],
        ['高中', '数学', 1, 1, 1],
        ['小学', '英语', 1, 8, 1],
        ['小学汇总', '小学汇总', 1, 1, 1],
        ['初中', '数学', 13, 1, 15],
        ['小学', '语文', 1, 1, 33],
        ['高中汇总', '高中汇总', 13, 1, 15]
    ]
    return head, titles, data


def run_example():
    # 排序用的年级学科列表
    sorted_grade = get_sort_list('年级')
    sorted_subject = get_sort_list('学科')

    # 常规表格
    head, titles, data = get_example_data()
    html1 = data_to_html(data, titles, head)

    # 排序
    # 先用第一列按年级排序,再用第二列按学科排序,不需要排序的列可以补空数组[],前面的列排序优先级高
    head, titles, data = get_example_data()
    data = sort_data(data, [sorted_grade, sorted_subject])
    html2 = data_to_html(data, titles, head)

    # 垂直合并表格
    head, titles, data = get_example_data()
    data = sort_data(data, [sorted_grade, sorted_subject])
    html3 = data_to_html(data, titles, head, 2, True, False)

    # 水平合并表格
    head, titles, data = get_example_data()
    data = sort_data(data, [sorted_grade, sorted_subject])
    html4 = data_to_html(data, titles, head, 2, False, True)

    # 垂直水平合并表格
    head, titles, data = get_example_data()
    data = sort_data(data, [sorted_grade, sorted_subject])
    html5 = data_to_html(data, titles, head, 2, True, True)

    # 垂直水平合并表格并自定义宽度
    head, titles, data = get_example_data()
    data = sort_data(data, [sorted_grade, sorted_subject])
    html6 = data_to_html(data, titles, head, 2, True, True, [160, 120, 0, 50])

    # 自定义颜色字号
    head, titles, data = get_example_data()
    data = sort_data(data, [sorted_grade, sorted_subject])
    html7 = data_to_html(data, titles, head, 2, True, True, [], True)

    # 各种功能都用上试试
    head, titles, data = get_example_data()
    data = sort_data(data, [sorted_grade, sorted_subject])
    html8 = data_to_html(data, titles, head, 2, True, True, [160, 120, 0, 50, 200], True)

    mail_to = '123@qq.com'
    subject = '邮件主题'
    msg_txt = html1 + html2 + html3 + html4 + html5 + html6 + html7 + html8
    send_mail(mail_to, subject, msg_txt)


if __name__ == '__main__':
    run_example()

使用方法:将以上代码存储为html_self.py,并放在运行的代码所在的文件夹,即可通过import html_self 导入使用,如果想要所有位置的代码都能导入使用,则可将html_self.py文件放在Python环境所在的Lib\site-packages文件夹下,可通过where python查看Python环境的路径。

测试函数如下:根据第10列的值判断字体颜色:黑色(>0)、蓝色(=0)、红色(<0),具体完整代码可参考我另一篇文章:Python搭建一套会议待办事项自动提醒程序_jogarys的博客-CSDN博客

ar_not_finish = html_self.data_to_html(artab_meeting.values,[list(artab_meeting.columns)],
                                             head=meeting,
                                             widths=[30,70,50,50,100,200,200,40,50,100,40,40],
                                             aligns=['center','center','center','center','center','left','left'],
                                             font=['微软雅黑','2','black','red',10,'blue'],
                                             font_title=['微软雅黑','2',''])

邮件输出表格如下:

小组例会

序号

会议

记录人

提出人

提出日期

待办事项

答复

重要程度

责任人

截止日期

剩余天数

待办状态

1

小组例会

张三

王五

2023-06-12

销售数据大数据分析

-

紧急

李四

2023-06-13

-1

Delay

3

小组例会

张三

王五

2023-06-14

产品二报告准备

-

一般

李四

2023-06-20

6

Open

6

小组例会

张三

王五

2023-06-15

重点事项1

-

重要

王五

2023-06-14

0

Open

7

小组例会

张三

王五

2023-06-15

重点事项2

-

重要

王五

2023-06-14

0

Open

8

小组例会

张三

王五

2023-06-15

重点事项3

-

重要

王五

2023-06-17

3

Open

9

小组例会

张三

王五

2023-06-15

重点事项4

-

重要

王五

2023-06-18

4

Open

10

小组例会

张三

王五

2023-06-15

重点事项5

-

重要

王五

2023-06-19

5

Open

11

小组例会

张三

王五

2023-06-15

重点事项6

-

重要

王五

2023-06-20

6

Open

12

小组例会

张三

王五

2023-06-15

重点事项7

-

重要

王五

2023-06-21

7

Open

本文参考了文章:用python生成邮件正文html表格_python邮件html表格_Godxv的博客-CSDN博客做了一些小修改和升级

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值