使用python读Excel文件并写入另一个xls模版

效果如下:

原文件内容

转化后的内容

大致代码如下:

1. load_it.py

#!/usr/bin/env python
import re
from datetime import datetime
from io import BytesIO
from pathlib import Path
from typing import List, Union

from fastapi import HTTPException
from openpyxl import load_workbook

RE_SPACES = re.compile(r"\s{2,}")


def slim(s: str) -> str:
    return RE_SPACES.sub(" ", s)


class ValidationError(HTTPException):
    def __init__(self, detail: str, status_code: int = 400):
        super().__init__(status_code, detail=detail)


def remove_author(s: str) -> str:
    if s := s.replace("作者:\n", "").replace("Administrator:\n", ""):
        return str(s)
    return ''


def read_excel(filename: Union[Path, str, bytes, BytesIO]):
    if isinstance(filename, bytes):
        filename = BytesIO(filename)
    return load_workbook(filename)


def load(filename: Union[Path, str, bytes, BytesIO]):
    wb = read_excel(filename)
    sheet_name = "工资表"
    try:
        sheet = wb[sheet_name]
    except KeyError:
        try:
            sheet = wb["Sheet1"]
        except KeyError:
            raise ValidationError(f"未找到名称为{sheet_name!r}的工作表")
    title = sheet.cell(1, 1).value.strip()
    now = datetime.now()
    if "月" in title:
        remark = title.split("年")[-1].strip("表").replace("份", "")
    else:
        if (month := now.month - 1) == 0:
            month = 12
        remark = f"{month}月工资"
    day = f"{now:%Y.%m.%d}"
    lines: List[list] = []
    for row in range(4, sheet.max_row):
        xuhao = sheet.cell(row, 1).value
        if xuhao and (isinstance(xuhao, int) or xuhao.isdigit()):
            name = sheet.cell(row, 2).value
            total = 0
            if (base := sheet.cell(row, 4).value) is None:
                base = "/"
            else:
                if isinstance(base, str):
                    if base.startswith("="):
                        base = eval(base[1:])
                    else:
                        raise TypeError(f"Expect int value, got: {base=}")
                total += base
            commission_comment = ""  # 提成批注
            commission_cell = sheet.cell(row, 5)
            if (commission := commission_cell.value) is None:
                commission = "/"
            else:
                if isinstance(commission, str) and commission.startswith('='):
                    commission = eval(commission[1:])
                total += commission
                if _cc := commission_cell.comment:
                    if _ct := _cc.text:
                        commission_comment = remove_author(_ct)
            if (attend := sheet.cell(row, 6).value) is None:
                if (attend := sheet.cell(row, 13).value) is None:
                    attend = "/"
            if (attend_money := sheet.cell(row, 7).value) is not None:
                total += attend_money
                attend = attend.strip().strip("+-/").strip()
                if attend_money > 0:
                    attend += f" +{attend_money}"
                else:
                    attend += f" {attend_money}"
            if (late := sheet.cell(row, 8).value) is None:
                late = "/"
            else:
                late = slim(late)
            if late_money := sheet.cell(row, 9).value:
                total += late_money
                if late_money > 0:
                    late = f"{late}{late_money}"
                else:
                    late = late.strip("/") + str(late_money)
            if subsidy_value := sheet.cell(row, 11).value:  # 补助
                if isinstance(subsidy_value, str) and subsidy_value.startswith("="):
                    subsidy_value = eval(subsidy_value[1:])
                try:
                    total += subsidy_value
                except TypeError:
                    raise ValidationError(
                        f"第{row}行第11列数据异常:预期为数值,得到的是{subsidy_value!r}"
                    )
            subsidy = "/"
            if _c := sheet.cell(row, 10).comment:
                if _s := _c.text:
                    subsidy = remove_author(_s)

            one = [
                name,
                base,
                commission,
                attend,
                late,
                subsidy,
                total,
                remark,
                day,
                commission_comment,
            ]
            lines.append(one)
    return lines


def main():
    import sys

    if not sys.argv[1:]:
        print("No args, do nothing.")
        return
    print(load(sys.argv[1]))


if __name__ == "__main__":
    main()

 

 2. gen_excel.py

#!/usr/bin/env python
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Tuple, Union

import xlrd
import xlwt
from xlutils.copy import copy as xls_copy

from load_it import load, read_excel, remove_author
from settings import BASE_DIR, MEDIA_ROOT

SAMPLE = "salary_tips.xls"
DataType = Union[int, float, str, None]


def cell_style(is_top: bool = False, is_bottom: bool = False, has_border=True):
    """单元格样式"""
    style = xlwt.XFStyle()
    # 字体大小,11为字号,20为衡量单位
    # font = xlwt.Font()
    style.font.height = 20 * 9

    align = xlwt.Alignment()
    # 0x01(左端对齐)、0x02(水平方向上居中对齐)、0x03(右端对齐)
    align.horz = 0x02
    # 0x00(上端对齐)、 0x01(垂直方向上居中对齐)、0x02(底端对齐)
    align.vert = 0x01
    # 设置自动换行
    align.wrap = 1
    style.alignment = align

    # 设置边框
    # 细实线:1,小粗实线:2,细虚线:3,中细虚线:4,大粗实线:5,双线:6,细点虚线:7
    # 大粗虚线:8,细点划线:9,粗点划线:10,细双点划线:11,粗双点划线:12,斜点划线:13
    if has_border:
        borders = xlwt.Borders()
        borders.left = 2
        borders.right = 2
        borders.top = 1 + is_top
        borders.bottom = 1 + is_bottom
        style.borders = borders
    return style


def boom(tips: List[List[Tuple[int, int, DataType]]]) -> str:
    """将数据填入模板生成Excel表"""
    sample = BASE_DIR / SAMPLE
    xls = xls_copy(xlrd.open_workbook(sample, formatting_info=True))
    ws = xls.get_sheet(0)
    style = cell_style()
    top_style = cell_style(is_top=True)
    bottom_style = cell_style(is_bottom=True)
    plain_style = cell_style(has_border=False)
    last_index = 8
    for datas in tips:
        for i, d in enumerate(datas[:-1]):
            if i == 0:
                ws.write(*d, top_style)
            elif i == last_index:
                ws.write(*d, bottom_style)
            else:
                ws.write(*d, style)
        if _tc := datas[-1]:
            row, col, text = _tc
            if text:
                ws.write_merge(row, row, col - 1, col, text, plain_style)
    fname = MEDIA_ROOT / f"gzt_{datetime.now():%Y%m%d%H%M%S}.xls"
    try:
        xls.save(fname)
    except TypeError as e:
        print("May be you can look at this to fix it:")
        print("https://blog.csdn.net/zhangvalue/article/details/105170305")
        raise e
    return str(fname).replace(str(BASE_DIR), "")  # 返回相对路径


def build_tips(lines: List[List[DataType]]):
    row_delta = 10  # 每隔10行填下一排的数据
    col_delta = 3  # 每隔3列填下一组数据
    line_tip = 5  # 每行有5个工资条
    row_begin = 0  # 从第一行开始
    col_begin = 1  # 从第二列开始填数据(第一列是固定的表头)
    tips = []
    for tip_index, tip in enumerate(lines):
        first_row = row_begin + tip_index // line_tip * row_delta
        col_index = col_begin + tip_index % line_tip * col_delta
        d = [
            (row_index + first_row, col_index, value)
            for row_index, value in enumerate(tip)
        ]
        tips.append(d)
    return tips


def burn_life(content: bytes) -> str:
    return boom(build_tips(load(content)))


def dear_sister(content: bytes, origin_name: Optional[str] = None) -> str:
    """2022-04-04 亲爱的妹妹想要一个可以把批注提取出来的"""
    wb = read_excel(content)
    sheet = wb.worksheets[0]
    count = 0
    # openpyxl的行和列都是从1开始
    for row in range(1, sheet.max_row):
        for col in range(1, sheet.max_column):
            cell = sheet.cell(row, col)
            if comment := cell.comment:
                if text := comment.text:
                    cell.value = remove_author(text)
                    count += 1
    if origin_name:
        fname = MEDIA_ROOT / f"{Path(origin_name).stem}-批注提取{count}.xls"
    else:
        fname = MEDIA_ROOT / f"批注提取{count}.xls"
    wb.save(fname)
    return str(fname).replace(str(BASE_DIR), "")  # 返回相对路径


def main():
    import sys

    if not sys.argv[1:]:
        print("No args, do nothing.")
        return
    if (p := Path(sys.argv[1])).is_file():
        lines = load(p.read_bytes())
    else:
        day = f"{datetime.now():%Y.%m.%d}"
        ss = [
            "狄仁杰",
            1600,
            360,
            "休5天,请假7.5天 -400",
            "迟到3次共16分钟",
            "扣社保-373\n工龄+100\n漏刷卡6次-300",
            987,
            "12月工资",
            day,
        ]
        lines = [ss, ss]
    print(boom(build_tips(lines)))


if __name__ == "__main__":
    main()

    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/76038.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【开发笔记】在Python中调用Docker,并运行SDK任务

目录 1 背景2 环境准备3 实现流程3.1 连接远程Docker3.1 创建容器3.2 解压SDK3.3 挂载容器卷3.4 运行任务3.5 判断任务状态3.6 容器的停止与销毁 4 可能遇到的问题 1 背景 使用Python,在远程Docker中创建一个容器,并在该容器中运行SDK任务 2 环境准备 …

Linux系列讲解 —— FTP协议的应用

简单介绍一下FTP文件传输协议在linux系统中的应用。 目录 0. 基本概念1. FTP Server1.1 安装FTP Server1.2 FTP Server开启和关闭1.3 查看FTP Server是否开启1.4 FTP服务器配置 2. FTP Client2.1 lftp2.2 ftp2.3 sftp2.4 文件资源管理器集成的ftp和sftp 3. ftp常用命令 0. 基本…

下一代计算:嵌入AI的云/雾/边缘/量子计算

计算系统在过去几十年中推动了计算机科学的发展,现在已成为企业世界的核心,提供基于云计算、雾计算、边缘计算、无服务器计算和量子计算的服务。现代计算系统解决了现实世界中许多需要低延迟和低响应时间的问题。这有助于全球各地的青年才俊创办初创企业…

如何安装Python?

如何安装Python? 安装Python非常简单,让我们一步步来进行。 1. 访问官方网站 首先,您需要访问Python官方网站(https://www.python.org/)。在首页上,您会看到一个大大的「Downloads」按钮,点击…

分布式 - 消息队列Kafka:Kafka消费者和消费者组

文章目录 1. Kafka 消费者是什么?2. Kafka 消费者组的概念?3. Kafka 消费者和消费者组有什么关系?4. Kafka 多个消费者如何同时消费一个分区? 1. Kafka 消费者是什么? 消费者负责订阅Kafka中的主题,并且从…

Python的变量命名规则是什么?

Python的变量命名规则 在Python中,变量是用来存储数据的,而变量命名是为了方便我们理解和引用这些数据。Python的变量命名规则相对灵活,但也有一些基本规则和约定,让我们一起来了解一下。 基本规则 只能包含字母、数字和下划线&…

一种多策略下RabbitMQ的延时队列实现

1.为什么会用到延时队列? 场景: 最近在开发一款系统中遇到这样一个场景,A系统开通套餐需要把套餐信息以邮件的形式发送给相关工作人员,经过人工审核通过后,在B系统里面开通,A系统会调B系统套餐列表接口查询套餐是否开通成功,开通成功则从A系统去完成订单,假如超过设定时间未开…

Unity 实现2D地面挖洞!涂抹地形(碰撞部分,方法二)

文章目录 前言一、初始化虚拟点1.1点结构:1.2每个点有的状态:1.3生成点结构: 二、实例化边缘碰撞盒2.1计算生成边缘碰撞盒 三、涂抹部分3.1.虚拟点3.2.鼠标点3.3.内圈3.4.外圈 四、关于优化结语: 前言 老规矩先上效果图 继上一篇涂抹地形文章讲解发出后,有不少网友…

Docker中MySQL应用部署操作步骤

在linux系统下安装mysql、安装redis是非常麻烦的,但是docker出现后,应用安装会非常简洁。 1.MySQL部署 2.docker中部署mysql的步骤 创建mysql容器 这样mysql就部署好了。 外部机器连接docker中部署的mysql

Intel 12代酷睿集体大降价!三折太离谱了

之前有德国媒体报道称,Intel 12/13代酷睿以及即将发布的14代酷睿,将会全面涨价。 没想到,12代酷睿大降价了,幅度相当不可思议,不过至少目前仅限美国市场,新蛋、亚马逊、MicroCenter等大型零售商集体行动。 …

Qt扫盲-QTableView理论总结

QTableView理论总结 一、概述二、导航三、视觉外观四、坐标系统五、示例代码1. 性别代理2. 学生信息模型3. 对应视图 一、概述 QTableView实现了一个tableview 来显示model 中的元素。这个类用于提供之前由QTable类提供的标准表,但这个是使用Qt的model/view架构提供…

四张图片道清AI大模型的发展史(1943-2023)

四张图片道清AI大模型的发展史(1943-2023) 现在最火的莫过于GPT了,也就是大规模语言模型(LLM)。“LLM” 是 “Large Language Model”(大语言模型)的简称,通常用来指代具有巨大规模参数和复杂架构的自然语言处理模型,…

代码随想录算法训练营第58天|动态规划part15|392.判断子序列、115.不同的子序列

代码随想录算法训练营第58天|动态规划part15|392.判断子序列、115.不同的子序列 392.判断子序列 392.判断子序列 思路: (这道题也可以用双指针的思路来实现,时间复杂度也是O(n)) 这道题应该算是编辑距…

java.lang.NoClassDefFoundError: org/apache/tez/dag/api/TezConfiguration

错误: java.lang.NoClassDefFoundError: org/apache/tez/dag/api/TezConfigurationat org.apache.hadoop.hive.ql.exec.tez.TezSessionPoolSession$AbstractTriggerValidator.startTriggerValidator(TezSessionPoolSession.java:74)at org.apache.hadoop.hive.ql.e…

MySQL 约束

查看约束 select * from information_schema.table_constraints where table_name要查看的表名按约束的作用范围 列级约束: 将此约束声明在对应字段的后面 表级约束:在表中所有字段都声明完,在所有字段的后面声明的约束,可以声明…

无涯教程-Perl - s函数

描述 这不是功能。这是正则表达式替换运算符。根据PATTERN中指定的正则表达式,将数据替换为REPLACE。与m //一样,分隔符由s后的第一个字符定义。 语法 以下是此函数的简单语法- s/PATTERN/REPLACE/返回值 如果失败,此函数返回0,如果成功,则返回替换次数。 例 以下是显示…

【C++深入浅出】初识C++上篇(关键字,命名空间,输入输出,缺省参数,函数重载)

目录 一. 前言 二. 什么是C 三. C关键字初探 四. 命名空间 4.1 为什么要引入命名空间 4.2 命名空间的定义 4.3 命名空间使用 五. C的输入输出 六. 缺省参数 6.1 缺省参数的概念 6.2 缺省参数的分类 七. 函数重载 7.1 函数重载的概念 7.2 函数重载的条件 7.3 C支…

PDM/PLM系统建设

仅供学习使用,会随时更新 工程机械跨生命周期数据管理系统 来源:清华大学 浅论企业PDM/PLM系统建设成功经验 来源:e-works 作者:陈凡 https://articles.e-works.net.cn/pdm/article149572.htm 随着“中国制造2025”强基工程战略的…

读高性能MySQL(第4版)笔记02_MySQL架构(下)

1. 事务日志 1.1. 事务日志有助于提高事务的效率 1.1.1. 存储引擎只需要更改内存中的数据副本,而不用每次修改磁盘中的表,这会非常快 1.1.2. 更改的记录写入事务日志中,事务日志会被持久化保存在硬盘上 1.2. 事务日志采用的是追加写操作&…

AUTOSAR规范与ECU软件开发(实践篇)3.3 AUTOSAR系统解决方案介绍(下)

示例将遵循AUTOSAR方法论来进行开发, 所用的AUTOSAR解决方案如图3.6所示。 图3.6 AUTOSAR系统解决方案 首先, 使用Matlab/Simulink来实现部分软件组件级的开发, 主要包括LightRequestSWC和LightControlSWC, 并自动生成应用层软件组…