#!/usr/bin/env python3 """生成样例文档: clean.docx(应返回空清单)与 drifted.docx(人为埋入 3 处漂移)。 用法: python make_samples.py [params.example.json] [输出目录] 正文与表格用 {{key}} / {{key:单位}} / {{key:单位:,}} 占位符从 params.json 渲染, 模拟「参数单一来源 + 模板渲染」的流程。drifted.docx 在渲染后手工改动 3 处: 1. 单位漂移: 表格 1「小时流量」行的单位格 m³/h 被改成 m³/d(数字 62.5 未变) 2. 修约漂移: 正文「吨水电耗 0.62 kWh/t」被写成 0.6(表格里仍是 0.62) 3. 派生量漂移: 表格 2「PAC年药剂费」仍是按旧水量 1200 t/d 算的 30660 元(应为 38325 元) """ import re import sys from pathlib import Path import docx from check_consistency import load_params HERE = Path(__file__).resolve().parent TITLE = "某工业园区 {{treated_flow_tpd}} t/d 污水处理站工程方案" BODY = [ ("h1", "1 项目概况"), ("p", "本工程设计处理水量 {{treated_flow_tpd}} t/d({{treated_flow_tpd:m³/h}} m³/h)," "进水COD {{cod_in_mgl}} mg/L,出水COD {{cod_out_mgl}} mg/L,COD去除率 {{cod_removal_pct}}%。"), ("p", "方案编制日期:2026年10月7日;出水执行 GB 18918-2002 一级A 标准。"), ("h1", "2 工艺设计"), ("p", "调节池水力停留时间 {{hrt_h}} h,共设 {{basin_count}} 座。"), ("p", "混凝段 PAC投加量 {{pac_dose_mgl}} mg/L,按设计水量计 PAC日耗量 {{pac_daily_kgd}} kg/d。"), ("p", "吨水电耗 {{power_kwh_per_t}} kWh/t,日耗电量 {{daily_power_kwhd}} kWh/d。"), ("p", "PAC单价 {{pac_price_yuan_kg}} 元/kg,PAC年药剂费 {{pac_annual_cost_wan}} 万元。"), ("h1", "3 投资估算"), ("p", "设备费 {{equipment_cost_wan}} 万元,安装费 {{install_cost_wan}} 万元," "工程总报价 {{total_quote_wan}} 万元。主要参数与费用见表 1、表 2。"), ] TABLE1 = [["序号", "指标", "数值", "单位"], ["1", "设计水量", "{{treated_flow_tpd}}", "t/d"], ["2", "小时流量", "{{treated_flow_tpd:m³/h}}", "m³/h"], ["3", "进水COD", "{{cod_in_mgl}}", "mg/L"], ["4", "出水COD", "{{cod_out_mgl}}", "mg/L"], ["5", "PAC投加量", "{{pac_dose_mgl}}", "mg/L"], ["6", "PAC日耗量", "{{pac_daily_kgd}}", "kg/d"], ["7", "吨水电耗", "{{power_kwh_per_t}}", "kWh/t"], ["8", "日耗电量", "{{daily_power_kwhd}}", "kWh/d"]] TABLE2 = [["序号", "项目", "金额"], ["1", "设备费", "{{equipment_cost_wan:元:,}} 元"], ["2", "安装费", "{{install_cost_wan}} 万元"], ["3", "PAC年药剂费", "{{pac_annual_cost_wan:元}} 元"], ["4", "工程总报价", "{{total_quote_wan}} 万元"]] def render(tpl, Q, cfg): def sub(m): key, unit, sep = (m.group(1).split(":") + ["", ""])[:3] q = Q[key] unit = unit or q["unit"] v = q["value"] * cfg["units"][q["unit"]][1] / cfg["units"][unit][1] d = q["unit_decimals"].get(unit, q["decimals"]) return f"{v:{sep}.{d}f}" return re.sub(r"\{\{([^{}]+)\}\}", sub, tpl) def build(out_path, params_path, drift=False): Q, cfg = load_params(params_path) body = [(kind, render(t, Q, cfg)) for kind, t in BODY] t1 = [[render(c, Q, cfg) for c in row] for row in TABLE1] t2 = [[render(c, Q, cfg) for c in row] for row in TABLE2] if drift: t1[2][3] = "m³/d" # 1. 单位 body = [(k, t.replace("吨水电耗 0.62", "吨水电耗 0.6")) for k, t in body] # 2. 修约 t2[3][2] = "30660 元" # 3. 派生量 doc = docx.Document() doc.add_heading(render(TITLE, Q, cfg), level=0) for kind, text in body: doc.add_heading(text, level=1) if kind == "h1" else doc.add_paragraph(text) for caption, rows in (("表 1 主要设计参数", t1), ("表 2 投资与运行费用", t2)): doc.add_paragraph(caption) table = doc.add_table(rows=len(rows), cols=len(rows[0])) table.style = "Table Grid" for r, row in enumerate(rows): for c, val in enumerate(row): table.cell(r, c).text = val doc.save(out_path) return out_path if __name__ == "__main__": params = sys.argv[1] if len(sys.argv) > 1 else str(HERE / "params.example.json") out = Path(sys.argv[2]) if len(sys.argv) > 2 else HERE print(build(out / "clean.docx", params)) print(build(out / "drifted.docx", params, drift=True))