使用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, Unionfrom fastapi import HTTPException
from openpyxl import load_workbookRE_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 = 12remark = f"{month}月工资"day = f"{now:%Y.%m.%d}"lines: List[list] = []for row in range(4, sheet.max_row):xuhao = sheet.cell(row, 1).valueif xuhao and (isinstance(xuhao, int) or xuhao.isdigit()):name = sheet.cell(row, 2).valuetotal = 0if (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 += basecommission_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 += commissionif _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_moneyattend = 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_moneyif 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_valueexcept 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 linesdef main():import sysif not sys.argv[1:]:print("No args, do nothing.")returnprint(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, Unionimport xlrd
import xlwt
from xlutils.copy import copy as xls_copyfrom load_it import load, read_excel, remove_author
from settings import BASE_DIR, MEDIA_ROOTSAMPLE = "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 * 9align = xlwt.Alignment()# 0x01(左端对齐)、0x02(水平方向上居中对齐)、0x03(右端对齐)align.horz = 0x02# 0x00(上端对齐)、 0x01(垂直方向上居中对齐)、0x02(底端对齐)align.vert = 0x01# 设置自动换行align.wrap = 1style.alignment = align# 设置边框# 细实线:1,小粗实线:2,细虚线:3,中细虚线:4,大粗实线:5,双线:6,细点虚线:7# 大粗虚线:8,细点划线:9,粗点划线:10,细双点划线:11,粗双点划线:12,斜点划线:13if has_border:borders = xlwt.Borders()borders.left = 2borders.right = 2borders.top = 1 + is_topborders.bottom = 1 + is_bottomstyle.borders = bordersreturn styledef boom(tips: List[List[Tuple[int, int, DataType]]]) -> str:"""将数据填入模板生成Excel表"""sample = BASE_DIR / SAMPLExls = 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 = 8for 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 = _tcif 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 ereturn 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_deltacol_index = col_begin + tip_index % line_tip * col_deltad = [(row_index + first_row, col_index, value)for row_index, value in enumerate(tip)]tips.append(d)return tipsdef 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 += 1if 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 sysif not sys.argv[1:]:print("No args, do nothing.")returnif (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()
相关文章:

使用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, Unionfrom fastapi import HTTPExcep…...
债务人去世,债权人要求其妻女承担还款责任,法院支持吗
债务人去世,债权人要求其妻女承担还款责任,法院支持吗 2019年9月20日,老张以公司资金周转为由向好友任某先后借款合计40万。2022年8月27日老张出具还款承诺书,承诺2022年11月30日前归还本息(本息90万元)到…...

arcgis pro3.0-3.0.1-3.0.2安装教程大全及安装包下载
一. 产品介绍: ArcGIS Pro 这一功能强大的单桌面 GIS 应用程序是一款功能丰富的软件,采用 ArcGIS Pro 用户社区提供的增强功能和创意进行开发。 ArcGIS Pro 支持 2D、3D 和 4D 模式下的数据可视化、高级分析和权威数据维护。 支持通过 Web GIS 在一系列 …...

@RequestHeader使用
RequestHeader 请求头参数的设置 GetMapping("paramTest/requestHeader")public String requestHeaderTest(RequestHeader("name") String name){return name;} 在Postman的Headers中添加请求头参数,不过貌似不能加中文...

LabVIEW开发图像采集和基于颜色的隔离
LabVIEW开发图像采集和基于颜色的隔离 在当今的工业和工厂中,准确性和精度是决定特定行业生产力的两个重要关键点。为了优化生产力,各行各业正在从手动操作转向自动操作和控制。机器人技术在工业过程中的出现为人类提供了机械辅助。机器视觉在工业机器人…...
站长公益主机,免费主机➕免费域名➕博客申请➕论坛申请
站长公益主机,免费主机➕免费域名➕博客申请➕论坛申请 在出教程之前准备好久,测试搭建轻量论坛无压力 选用稳定免费域名➕免费主机分销给,可以套CDN使用 坚持免费时间是大厂不能媲美,刚开始做网站时用的是这个分销,独…...

【PRO-UPDATE】自动更新程序图形小记
大纲流程 设计流程 v0.1 v1.0...
flume系列之:监控Systemctl托管的flume agent组
flume系列之:监控Systemctl托管的flume agent组 一、需求背景二、相关技术博客三、远程登陆flume机器四、发送飞书告警五、监控flume agent组状态一、需求背景 flume接kafka集群,一个kafka集群对应一个flume agent组,会把一组flume agent用systemctl托管每接一个kafka集群会…...

16.3.1 【Linux】程序的观察
既然程序这么重要,那么我们如何查阅系统上面正在运行当中的程序呢?利用静态的 ps 或者是动态的 top,还能以 pstree 来查阅程序树之间的关系。 ps :将某个时间点的程序运行情况撷取下来 仅观察自己的 bash 相关程序: p…...
HarmonyOS 设置全屏NoTitleBar
这篇很有用:玩转HarmonyOS 状态栏&标题栏&导航栏相关操作方法整理 配置页面全屏显示(在config.json中配置): "metaData": {"customizeData": [{"name": "hwc-theme","value": "androi…...
Java 模块解耦的设计策略
Java 平台模块系统 (JPMS) 提供了更强的封装、更高的可靠性和更好的关注点分离,有些同学可能没注意到。 不过呢,也是有利有弊。由于模块化应用程序构建在依赖其他模块才能正常工作的模块网络上,因此在许多情况下,模块彼此紧密耦合…...

支持https访问
文章目录 1. 打开自己的云服务器的 80 和 443 端口2. 安装 nginx3. 安装 snapd4. 安装 certbot5. 生成证书6. 拷贝生成的证书到项目工作目录7. 修改 main.go 程序如下8. 编译程序9. 启动程序10. 使用 https 和端口 8081 访问页面成功11. 下面修改程序,支持 https 和…...

JavaScript 中常用简写技巧总结
平时我们写代码时最高级的境界是自己写的东西别人看不懂!哈哈哈!分享一些自己常用的js简写技巧,长期更新,会着重挑选一些实用的简写技巧,使自己的代码更简洁优雅~ 这里只会收集一些大多数人不知道的用法,但…...

第15集丨Vue 江湖 —— 组件
目录 一、为什么需要组件1.1 传统方式编写应用1.2 使用组件方式编写应用1.3 Vue的组件管理 二、Vue中的组件1.1 基本概念1.1.1 组件分类1.1.2 Vue中使用组件的三大步骤:1.1.3 如何定义一个组件1.1.4 如何注册组件1.1.5 如何使用组件 1.2 注意点1.2.1 关于组件名1.2.2 关于组件标…...

【JVM】CPU飙高排查方案与思路
文章目录 CPU飙高排查方案与思路 CPU飙高排查方案与思路 1.使用top命令查看占用cpu的情况 2.通过top命令查看后,可以查看是哪一个进程占用cpu较高,上图所示的进程为:40940 3.查看进程中的线程信息 4.可以根据进程 id 找到有问题的线程&a…...

使用公网访问内网IIS网站服务器【无需公网IP】
使用公网访问内网IIS网站服务器【无需公网IP】 文章目录 使用公网访问内网IIS网站服务器【无需公网IP】前言1. 注册并安装cpolar2. 创建隧道映射3. 获取公网地址 前言 这里介绍通过内网穿透,实现公网访问内网IIS网站服务器。 都知道,现在基本不会被分配…...

Vim学习(二)—— 编译C程序
打开终端,这里以MobaXterm为例, 邮件创建新的空文件并命名, 然后cd到对应路径下,用 vim hello.cvim打开创建的文件,进入编辑模式,编辑完程序后按Esc退出编辑模式,输入 :wq保存并退出…...
【maven】常见命令
文章目录 1. 打包编译时跳过测试2.显示maven依赖树3. 显示maven依赖列表4. 下载依赖包的源码5. 安装本地jar到本地仓库 1. 打包编译时跳过测试 mvn clean install -DskipTests mvn clean install -Dmaven.test.skiptrueDskipTests,不执行测试用例,但编译…...
vue单项数据传输流式回复功能,post传值可关闭请求(@microsoft/fetch-event-source)
需求:实现一个类似于文心一言ai回复功能,一个字一个字往外蹦,不使用websocket还有什么其他方案呢?经过查询有一个 microsoft/fetch-event-source单向传输协议(服务端传输客户端)。废话不多说,上…...
“深入探究JVM内部机制:理解Java虚拟机的工作原理“
标题:深入探究JVM内部机制:理解Java虚拟机的工作原理 摘要:本文将深入分析Java虚拟机(JVM)的工作原理,包括类加载、内存管理、垃圾回收和即时编译等方面。通过详细解释这些概念,并给出示例代码…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...