新闻详情

新闻详情

首页 / 资讯中心 / 详情

检测报告批量改名分发,客户命名规则各不同,手工还容易发错

发布时间:2026/9/27 22:41:45来源:尧图网络
检测报告批量改名分发,客户命名规则各不同,手工还容易发错
报告一批批出齐分发这一步才开始按客户要的文件名改一遍、一份报告发给两个收件人、纸质件按合同寄几份、发完再回头把发放登记表补齐。手工做法是复制、改名、粘进客户文件夹再对着台账填表——名字改错一个客户收到的就是别人的报告登记表漏一行追溯时拿不出证据。为了解决这个问题Python 提供了 pathlib 和 shutil配上 openpyxl 写登记表三份表加一个原件目录读进来按每家客户的模板改名、按客户拆包顺手生成发放登记表与客服分发清单。本文按四步走配置路径与状态常量、读四份东西、按客户模板改名并挑出不能发的、写登记表与分发清单。仓库里两个 pyrun.py跑主线source/build_fixtures.py造数据自动装依赖、写日志、回读校验是外壳不逐行贴。一、卡在哪名字是客户定的记录是制度定的第三方检测是「一单客户独立走一遍」洽询、送样、收样通知、检测、报告、关账。报告出来以后这一段制度上要登记发放编号、数量、领取单位、领取人、发放日期客户那边又对文件名各有要求。两头都得满足手工做就容易出错。卡点手工做法本文做法每家客户命名要求不一样一家记一套改的时候翻记录一行命名模板 占位符改模板不改代码客户名尾巴粘着全角空格被当成两家收件人查不到客户名先压成比较键报告待审核、已作废夹在中间一起发出去状态先判挂起的不占分发件一份报告两个收件人、纸质要三份登记表只记一行按「报告 × 收件人」展开份数分方式给二、四步走第 1 步配置路径与状态常量。路径从脚本自己的位置长出来拷到别的电脑不用改。三个常量是刻意的发放日期写死不许用date.today()——隔天重跑同一批报告日期就变了跟客服手上的邮件时间对不上不能分发的状态单独列表作废件流出去是合规事故非法字符统一替换产品名里的斜杠不换连文件都建不出来。出处run.py顶部的路径与状态常量区from pathlib import Path这类顶层 import 都在文件顶部。ROOT Path(__file__).resolve().parent RAW_DIR ROOT / 01_raw_data OUT_DIR ROOT / 02_output TPL_DIR ROOT / source / templates LOG_FILE ROOT / source / run_log.txt ORIG_DIR RAW_DIR / 报告原件 REPORT_CSV RAW_DIR / 报告台账.csv NAMING_CSV RAW_DIR / 客户命名规则.csv RECIPIENT_CSV RAW_DIR / 客户收件人.csv TPL_XLSX TPL_DIR / 报告发放登记表.xlsx LEDGER_XLSX OUT_DIR / 发放登记台账.xlsx MANIFEST_CSV OUT_DIR / 客户分发清单.csv PACK_DIR OUT_DIR / 报告分发 # 发放日期登记表上「发放日期」这一栏填哪天得是定的。 # **不许用 date.today()**——隔天重跑同一批报告的发放日期就变了跟客服手上的邮件时间对不上。 SEND_DAY date(2026, 9, 25) # 不对外分发的报告状态。作废报告的电子件流出去是合规事故宁可挂起也不改名。 BLOCK_STATUS {待审核: 报告未出具不能分发, 已作废: 报告已作废不对外分发} # 文件名里不许出现的字符。产品名、委托单号里都可能带斜杠不换掉连文件都建不出来。 ILLEGAL_RE re.compile(r[\\/:*?|\r\n\t]) LEFTOVER_RE re.compile(r\{[^}]*\}) # 拼完还剩花括号 模板里有不认识的字段 DATE_FMTS (%Y-%m-%d, %Y/%m/%d, %Y.%m.%d, %Y%m%d) DATE_FMT yyyy-mm-dd # 写进 Excel 的日期格要顺手设格式否则显示成序列号第 2 步读四份东西含归堆。台账一行一份报告命名规则按客户名收成字典收件人收成「客户 → 收件人列表」——一家客户可能有两个收件人。读 CSV 有两个小动作必须做第一列为空的行整行跳过表尾合计行、说明行第一列都是空的说明文字写进第一列就多出一份报告原件编号按字符串处理、不做int()老系统留下的编号是0000122转成整数就变122跟台账永远对不上。出处run.py的read_csv_rows()读数据一节最前面、read_reports()/read_naming()/read_recipients()紧跟其后与scan_originals()读数据一节末尾。def read_csv_rows(path): 读 CSV。第一列为空的行一律跳过——表尾的合计行、说明行第一列都是空的 说明文字要是写进第一列就会被当成一份报告混进分发清单。 with open(path, r, encodingutf-8-sig, newline) as f: rows list(csv.reader(f)) header [clean(x) for x in rows[0]] out [] for raw in rows[1:]: if not raw or not clean(raw[0]): continue out.append({header[i]: clean(raw[i]) if i len(raw) else for i in range(len(header))}) return outdef scan_originals(): 扫报告原件目录用文件名去扩展名当报告编号。 文件名一律按字符串处理**不做 int 转换**——老系统留下的编号是 0000122 转成整数就变成 122跟台账永远对不上人还看不出是哪儿错了。 found {} for path in sorted(ORIG_DIR.glob(*.pdf)): found[path.stem] path return found第 3 步判定能不能发再拼文件名。判定顺序就是业务顺序一步都错不得动作为什么这么写先判状态再判别的作废件不管名字对不对都不能发顺序反了挂起理由会张冠李戴拼完再查一遍残留的花括号客户模板里字段名写错一个字带花括号的文件名就发给客户了撞名两份都挂起只挂后一份前一份照样发出去两家客户的报告就混了出处check_report()分发判定一节开头、make_filename()紧跟其后、plan()末段。def check_report(rep, naming, recipients, originals): 判定顺序就是业务顺序报告没出 → 不说名字的事客户没登记收件人 → 不判原件 原件不在 → 不拼名字。 顺序反了挂起理由就会张冠李戴一份还没审核的报告被标成「原件缺失」 客服拿着错理由去补资料补完还是发不出去。 if rep[状态] in BLOCK_STATUS: return 挂起, rep[状态], BLOCK_STATUS[rep[状态]] rule naming.get(cust_key(rep[客户名称])) if rule is None: return 挂起, 客户命名规则缺失, 台账里的客户在命名规则表里查不到不知道按谁的规矩改名 if not recipients.get(cust_key(rep[客户名称])): return 挂起, 未登记收件人, 客户收件人表里没有这一家报告不知道发给谁 if clean(rep[报告编号]) not in originals: return 挂起, 原件缺失, 报告原件目录里找不到这份报告改不了名 if rep[报告日期] is None: return 挂起, 报告日期认不出, 日期列写法不在支持列表里不能用它拼文件名 return 正常, , def make_filename(rep, rule): 按客户给的模板拼文件名先换占位符再换非法字符最后统一加 .pdf。 拼完必须回头查一遍还有没有没换掉的花括号——客户把字段名写错一个字 {产品名}不检查就会把一个带花括号的文件名发给客户。 name rule[模板] for key in (报告编号, 委托单号, 产品名称): name name.replace({%s} % key, clean(rep[key])) name name.replace({客户}, clean(rule[写客户]) or clean(rep[客户名称])) name name.replace({报告日期}, rep[报告日期].strftime(rule[日期写法])) if LEFTOVER_RE.search(name): raise ValueError(f模板里有不认识的占位符{LEFTOVER_RE.findall(name)}) return ILLEGAL_RE.sub(-, name).strip() .pdfdup {k for k, group in keys.items() if len(group) 1} if dup: kept [] for item in items: if item[键] in dup: held.append({报告编号: item[rep][报告编号], 客户名称: item[rep][客户名称], 问题: 命名撞名, 说明: f同一家客户下另有报告算出同名文件 {item[文件名]} 两份都挂起先跟客户确认改名规则}) else: kept.append(item) items kept return items, held第 4 步出分发件、写登记表。分发件按客户建目录、把原件复制过去登记表按「报告 × 收件人」一行份数按发放方式给——纸质按合同份数电子只记一份多记会让台账数量虚高。最后给客服一份分发清单照着发就行。登记表往模板里填表头行按栏目名反查模板上加标题行、改列顺序都不影响。出处pack()输出一节最前面、ledger_rows()紧跟其后、write_manifest()输出一节末尾。def pack(items, originals): 按客户建目录、把原件**复制**成分发件。 复制不是移动原件是归档凭证动它等于动了可追溯性分发件只是副本 目录每次重跑先清空免得上一版留下的文件被当成这一版发出去。 if PACK_DIR.exists(): shutil.rmtree(PACK_DIR) rows [] for item in items: cust_dir PACK_DIR / ILLEGAL_RE.sub(-, clean(item[rep][客户名称])) cust_dir.mkdir(parentsTrue, exist_okTrue) shutil.copy2(originals[clean(item[rep][报告编号])], cust_dir / item[文件名]) rows.append(item) return rowsdef ledger_rows(items): 发放登记表按「报告 × 收件人」一行——制度里的登记表记的是**每一次发放动作** 一份报告发给两个人就是两次发放。 份数按发放方式给纸质按委托约定的份数台账里的报告份数电子只记 1 份。 电子件多记份数会让台账上的数量虚高对不上客户实际拿到的纸。 rows [] for item in items: rep item[rep] for rec in item[收件人]: paper rec[发放方式] 纸质 rows.append([rep[报告编号], rep[产品名称], rep[报告份数] if paper else 1, rep[客户名称], rec[收件人], rec[电话], rec[发放方式], SEND_DAY, item[文件名], , f纸质件按 {rep[报告份数]} 份寄出 if paper else 电子件加密发送]) return rowsdef write_manifest(items): 给客服一份分发清单哪个收件人、要发哪几份、邮箱是哪个照着发就行。 groups {} for item in items: for rec in item[收件人]: key (item[rep][客户名称], rec[收件人], rec[发放方式], rec[邮箱]) groups.setdefault(key, []).append(item[文件名]) with open(MANIFEST_CSV, w, encodingutf-8-sig, newline) as f: w csv.writer(f) w.writerow([客户名称, 收件人, 电子邮箱, 发放方式, 报告数, 分发文件, 建议邮件主题]) for (cust, who, way, mail), files in groups.items(): w.writerow([cust, who, mail, way, len(files), .join(sorted(files)), f【检测报告】{cust} 报告已出具{len(files)} 份]) return groups三、踩过的坑都在这份数据上复现过坑现象怎么改判定顺序排错没登记收件人的客户全被挂「未登记收件人」撞名分支根本没跑到等于没测按业务顺序排状态 → 命名规则 → 收件人 → 原件 → 日期编号转成整数0000122变122跟台账对不上编号全程当字符串date写进 Excel、读回是datetime回读校验假失败比之前先.date()表尾说明文字放进第一列台账上多出一份不存在的报告说明行第一列留空分发件用移动而不是复制原件目录被搬空归档凭证没了复制重跑先清空分发目录总结这段活的难点不是改名是两边都不许错客户要的文件名得对得上制度要的发放记录一行不能少。所以脚本分三件事——按客户模板改名、把不能发的挑出来挂起、把登记表和分发清单一起生成。判定顺序按业务排挂起理由才指向该补的东西。配套示例可复跑台账 11 行 → 分发件 4 份 → 登记 6 行 → 挂起 7 份回读校验 26 项全过。完整源码本文配套的可运行示例已开源把01_raw_data/里的表换成自己机构的克隆下来直接复跑huang_jianhua0101/examples - Gitee.com关于我在实验室一线待了 13 年9 年制药 4 年第三方检测做的一直是实验室信息化。做过 STARLIMS 的甲方 PM——一期、二期两轮上线都由我主导招标到 3Q 验证到验收全流程也在系统上自己做过二次开发——把纸质的账号申请流程搬到线上跑在 STARLIMS 之前还有 6 年多 CS 架构 LIMS 的使用与运维经验其中一段经 Citrix 远程接入。现在专做实验室里那些重复劳动报表自动生成、仪器数据对接、合规文档批量处理。本科物理化学、硕士计算机化学既听得懂 QA 说的变更控制也看得懂仪器导出的原始数据长什么样。SOP、偏差、OOS、样本流转这些词不用你解释。现在主要做这几类- 检验报告与台账批量生成模板不动数据自动填格式一步不错- 仪器数据对接色谱、光谱、酶标仪导出的原始文件解析、清洗、入库、转成报表- 合规文档自动化SOP、验证方案、批记录这类重复文档的批量生成与核对- 数据完整性核查按 ALCOA 逐条核对原始数据与记录是否对得上手里有这类活儿卡着或者只是想问问能不能自动化都欢迎评论区聊先把问题说清楚再谈怎么做。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

OpenCV笔迹识别系统实战:从图像预处理到特征匹配的完整实现 2026/9/28 1:20:02

OpenCV笔迹识别系统实战:从图像预处理到特征匹配的完整实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
FPGA中CORDIC IP核实现sin/cos计算:配置、仿真与避坑指南 2026/9/28 1:20:02

FPGA中CORDIC IP核实现sin/cos计算:配置、仿真与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
LA664上CAS重试死循环导致事务回滚与数据丢失的深度复盘 2026/9/28 1:20:02

LA664上CAS重试死循环导致事务回滚与数据丢失的深度复盘

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
MediaPipe手部面部关键点驱动Unity虚拟人实战指南 2026/9/28 1:20:02

MediaPipe手部面部关键点驱动Unity虚拟人实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
S905L3刷机工具v2.2.5与v2.2.7版本对比实测 2026/9/28 1:20:02

S905L3刷机工具v2.2.5与v2.2.7版本对比实测

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
SoC模块验证规格:结构化作战地图与覆盖率缝合方法 2026/9/28 1:19:56

SoC模块验证规格:结构化作战地图与覆盖率缝合方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉