183 lines
12 KiB
Python
183 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
单校区开工闸门 / Campus Workflow Gate
|
|
=====================================
|
|
南通新东方租赁合同梳理——每开一个新校区,动手前必须先跑这个脚本。
|
|
|
|
为什么存在:防止"开新校区时凭上个校区的印象乱跑/跳步"。
|
|
这是物理闸门——脚本把【从头到尾的完整流程】+【单校区独立闭环纪律】打印出来,
|
|
逼自己逐条确认,不跑不准动手填表。
|
|
|
|
🔴 通读强制纪律(悦拾光0703教训,详见 references/fulltext-reading-discipline-0703.md):
|
|
Step2动作A法律审查必须 read_file 从第1行读到最后一行(每批500行),不得 grep 代替。
|
|
I列按21/15固定类目逐项摘录原文(格式:· [类目] 原文(条款号))。
|
|
建完表后跑 scripts/i-column-coverage-check.py 报警核查覆盖率。
|
|
|
|
用法:
|
|
python3 campus-workflow-gate.py <校区名>
|
|
例:
|
|
python3 campus-workflow-gate.py 人民中路
|
|
|
|
它会:
|
|
1. 打印「单校区独立闭环纪律」——本校区从 Step0 从头做,不受其他校区影响
|
|
2. 打印完整 Step 0→7 workflow + 三角色 + 交付物板块的强制清单
|
|
3. 定位该校区在 Nextcloud 的源文件夹 + 汇总表应存放的位置(=校区自己的文件夹)
|
|
4. 生成一份待打勾的 todo 文本,贴进 todo 工具
|
|
"""
|
|
import sys, os, subprocess
|
|
|
|
NC_CONTAINER = "nextcloud-nextcloud-1"
|
|
NC_BASE = "/var/www/html/data/admin/files/小Maggie协作区/南通新东方/履约期内非集采合同-综办/房租物业合同"
|
|
NC_DAV = "小Maggie协作区/南通新东方/履约期内非集采合同-综办/房租物业合同"
|
|
|
|
def campus_dir_exists(campus):
|
|
p = f"{NC_BASE}/{campus}"
|
|
r = subprocess.run(["docker","exec",NC_CONTAINER,"test","-d",p],
|
|
capture_output=True)
|
|
return r.returncode == 0
|
|
|
|
def list_campus_files(campus):
|
|
p = f"{NC_BASE}/{campus}"
|
|
r = subprocess.run(["docker","exec",NC_CONTAINER,"bash","-c",f'ls -la "{p}"'],
|
|
capture_output=True, text=True)
|
|
return r.stdout
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("用法: python3 campus-workflow-gate.py <校区名>")
|
|
print("例: python3 campus-workflow-gate.py 人民中路")
|
|
sys.exit(1)
|
|
campus = sys.argv[1].strip()
|
|
|
|
bar = "=" * 70
|
|
print(bar)
|
|
print(f" 单校区开工闸门 · 校区 = 【{campus}】")
|
|
print(bar)
|
|
|
|
# ---- 第一道:独立闭环纪律 + 地基四铁律 ----
|
|
print("""
|
|
🔴🔴 单校区独立闭环纪律(Maggie 2026-06-23 立,开工前默念)🔴🔴
|
|
1. 本校区从 Step 0 从头做到 Step 6 独立跑一遍(Step 7 总览是全部校区定稿后的全局收尾)。
|
|
2. 绝不受其他校区影响——不拿"上个校区做过/世茂悦拾光是这样"的印象代替
|
|
本校区的逐字通读、逐条审查、回 07 原件比对。每个校区都是第一次。
|
|
3. 别的校区的结论、定级、措辞,统统不假设适用于本校区;一切回本校区原文。
|
|
4. 这是一次"全新合同全面审",不是"套上一份的模子"。
|
|
|
|
🔴🔴 地基四铁律(Maggie 2026-06-26 重申,优先级最高)🔴🔴
|
|
1. 逐字逐句:亲自读完整篇OCR,一字不跳。OCR乱码停→vision核实,不准跳过猜值。
|
|
🔴 Step1完成后必跑 ocr-garble-detect.py 扫全文乱码,高危行逐条vision消灭(教训:
|
|
凤凰文化10.2乱码未核实→整段"初年年租金20%违约金"丢失→审查结论反转→返工)
|
|
2. 整体理解:通读全文后再逐条审,先建立全文结构认知。
|
|
3. 上下文联系:每读一条问"这条被别处限定/修改了吗?"
|
|
4. 逻辑分析:数字、比例、日期、主体用逻辑推一遍——合理吗?自洽吗?
|
|
""")
|
|
|
|
# ---- 第二道:完整流程强制清单 ----
|
|
print("""———— 完整 WORKFLOW(缺一步不交付)————
|
|
Step 0 文件盘点归类:按文件夹结构(房租/扩租/物业),含空目录,不跨夹重排
|
|
Step 1 取 PDF + OCR→.md(大文件后台跑;纯扫描件文字层=0 必 OCR)
|
|
Step 2 承办(四眼分离前半)【并行,不串行】:
|
|
⚡ 开工第一动作 = 立刻 delegate_task 发动作B 到后台,发出的同一秒自己开读动作A
|
|
· 动作B 提取分析 = subagent(后台先发):OCR要素+模版比对+退租敞口+填表初稿
|
|
· 动作A 法律审查 = 小Maggie本人主审(B发出后立即开读),亲自 read_file 逐字通读全文,
|
|
八维框架,当全新合同审(独立法律审查框架)—— 两件事同时跑,绝不先做完A再做B
|
|
· 🔴 模版比对必须回 07-房屋租赁合同.docx 原件逐条核(subagent context 带原件路径)
|
|
Step 3 写 Excel:12 列(K=法律风险/L=模版差异,物理分列);
|
|
按文件夹分板块;末尾必有「整体风险分析与建议」段
|
|
· 各列规则详见 references/column-rules-0701.md(Maggie校准版,优先级最高)
|
|
· H列四检:①条款号 ②月租换算 ③付款推算 ④❗标注。缺一不过,不过不交付。
|
|
· 需客户核实内容整条标红(富文本红是最后一步→WPS另存/sharedStrings XML层)
|
|
Step 4 三角色校对:法律校对 ‖ 格式校对(六维清单)→ 闭环复核
|
|
Step 5 小Maggie终审:合并法律风险栏、回07原件复核L列、确认问题闭环、最后把关
|
|
· 🔴 K/L 分工自检(必跑):K列 grep "模版|07模版|07-房屋"=0(独立审查不引模版,
|
|
匹配"07模版"而非裸07防误命中金额数字);L列 grep "风险|建议|不利|详见"=0(纯客观不下判断)。
|
|
任一非0即回去拆分。判据=遮模版测试:把"模版怎么写"遮住,K列风险论述仍完整成立才算独立。
|
|
Step 6 交付前自查+存档:x2t渲染PDF + pdftotext拍平grep验文字 + vision验视觉(图先压<400KB)
|
|
→ 汇总表存本校区文件夹 + files:scan + 清OO缓存 → 发Maggie核
|
|
〔全部校区定稿后〕Step 7 整合总览sheet(全局收尾,非单校区步骤)
|
|
———— 交付物必含板块(验收必查)————
|
|
逐条风险(🔴🟡🟢分级,标条款号) + 整体风险分析与建议段 + L列模版差异(回07原件)
|
|
""")
|
|
|
|
# ---- 第三道:存放纪律 + 定位 ----
|
|
print(bar)
|
|
print(" 📁 汇总表存放纪律(Maggie 2026-06-23 立)")
|
|
print(bar)
|
|
exists = campus_dir_exists(campus)
|
|
if exists:
|
|
print(f"✅ 校区源文件夹已定位:")
|
|
print(f" 容器路径: {NC_BASE}/{campus}/")
|
|
print(f" WebDAV : {NC_DAV}/{campus}/")
|
|
print(f"\n 本校区文件清单:")
|
|
for line in list_campus_files(campus).splitlines():
|
|
if line.strip() and not line.startswith("total"):
|
|
print(f" {line}")
|
|
else:
|
|
print(f"⚠️ 未在标准路径找到校区目录【{campus}】。请先核对校区名,或确认目录是否在别处:")
|
|
print(f" 预期: {NC_BASE}/{campus}/")
|
|
print(f" (17 个校区均在 房租物业合同/ 下,标准名单:")
|
|
print(f" 万达、世茂、人民中路、凤凰文化、北翼玖玖、南通大厦、小石桥晏园、悦拾光、")
|
|
print(f" 星月、桃坞路、解放中路、跃龙路、通大、通大附、通州金鹰、金飞达、龙信)")
|
|
print(f"""
|
|
🔴 本校区汇总表【必须】存到本校区自己的文件夹下,不放别处、不放公共目录:
|
|
存放路径: {NC_DAV}/{campus}/
|
|
命名规则: {campus}-梳理-MJ-YYYYMMDD.xlsx (当事人/项目名+文件名+修改人+日期)
|
|
—— 每个校区的汇总表归到各自校区文件夹,与该校区合同放一起,便于客户对照查阅。
|
|
""")
|
|
|
|
# ---- 第四道:吐出 todo 文本 ----
|
|
print(bar)
|
|
print(" ⬇️ 把下面这份 todo 贴进 todo 工具,逐项打勾(缺一项不交付)")
|
|
print(bar)
|
|
todos = [
|
|
f"[{campus}] Step0 文件盘点归类(含空目录,不跨夹重排;多租赁物先按租赁物分类再按签约时间排列;详细K/L/I放最早签约那份行里后续写同上)",
|
|
f"[{campus}] Step1 取PDF+OCR→.md(纯扫描件必OCR)+保存.md到同目录 → 跑 ocr-garble-detect.py 扫乱码 → 高危乱码每处vision核实消灭 → 全灭才进Step2",
|
|
f"[{campus}] Step2 承办【并行,不串行】⚡开工第一动作=立刻 delegate_task 发动作B(模版比对回07原件,仅租赁合同需比对,物业等其他合同不需要)到后台 → 发出的同一秒自己开读动作A(本人逐字通读全文+八维当全新合同审)。两件事同时跑,绝不先做完A再做B",
|
|
f"[{campus}] Step3 写12列Excel(K法律风险/L模版差异分列)+H列四检+整体风险分析段+标红。🔴必须用 templates/single-campus-builder.py 照抄改值,禁裸写 openpyxl。🔴建L列前必read_file subagent比对文件,从里面逐条摘差异。🔴各列规则见 references/column-rules-0701.md(多合同时详细K/L放最早签约那份行里,后续行写同上)",
|
|
f"[{campus}] Step4 三角色校对(法律‖格式)闭环复核",
|
|
f"[{campus}] Step5 终审:合并法律风险+回07原件复核L列+K/L分工自检(K列grep'模版|07'=0,L列grep'风险|建议|不利|详见'=0)+确认闭环",
|
|
f"[{campus}] Step6 交付自查(x2t渲染+pdftotext验文字+vision验视觉)+存本校区文件夹",
|
|
f"[{campus}] 存档:汇总表存到本校区文件夹 {campus}/ + files:scan + 清OO缓存",
|
|
]
|
|
for i, t in enumerate(todos, 1):
|
|
print(f" {i}. {t}")
|
|
print()
|
|
|
|
# ---- 🔴 第五道:物理卡口(防跳步)----
|
|
print(bar)
|
|
print(" 🔴🔴 物理卡口(以下两条不做到 = 返工,不交付)🔴🔴")
|
|
print(bar)
|
|
print("""
|
|
卡口① 格式强制:Step3 写表必须用模板脚本
|
|
→ 路径:~/.hermes/skills/legal/contract-portfolio-analysis/templates/single-campus-builder.py
|
|
→ 方法:cp 到工作目录,改 TODO 标记的值(标题、项目信息、D5..L5、D8..L8、整体段、输出路径)
|
|
→ 禁止:自己 openpyxl 裸写样式、自创颜色、改列头措辞
|
|
→ 自检:交付前打开文件,和人民中路定稿并排对比——标题酒红底白字?段标题红底?行2灰底?
|
|
K列头="法律风险(站乙方立场)"?L列头="与07标准模版差异"?冻结A5?行高30/76/409.5?
|
|
|
|
卡口② 模版比对强制:Step2 动作B 必须 delegate_task subagent
|
|
→ 不能:自己读07模版后手写L列差异(会漏、会简略、会凭印象)
|
|
→ 做法:OCR完成后立即 delegate_task,context 含 07模版路径 + 两份合同OCR路径
|
|
→ 🔴 建表前必做:read_file 读 subagent 比对文件全文,L列从里面逐条摘、不从脑子里摘
|
|
→ 自检①:subagent 返回的差异清单是否 ≥ 20 条?
|
|
→ 自检②:L列每条差异是否都能在 subagent 比对文件里找到原文对应?
|
|
"甲方制式格式,与07模版不同"这种一句话概括 = 没读 subagent 文件 = 返工
|
|
subagent 抓到的差异(如举报邮箱新增、供电功率矛盾、首期期间变更)
|
|
你在 L 列里没写 = 没读 subagent 文件 = 返工
|
|
|
|
卡口③ 交付前格式自检(跑脚本,不凭眼):
|
|
→ python3 scripts/kl-separation-check.py <out.xlsx> # K列零模版、L列零判断词
|
|
→ python3 -c "import openpyxl; wb=openpyxl.load_workbook('<out.xlsx>');
|
|
ws=wb[wb.sheetnames[0]];
|
|
assert ws.cell(1,1).fill.start_color.rgb=='FF8B1A2B','标题不是酒红底';
|
|
assert ws.cell(3,1).fill.start_color.rgb=='FFC0504D','段标题不是红底';
|
|
assert ws.cell(4,11).value=='法律风险(站乙方立场)','K列头不对';
|
|
assert ws.cell(4,12).value=='与07标准模版差异','L列头不对';
|
|
print('OK')"
|
|
""")
|
|
print("提醒:开工前默念独立闭环纪律——本校区从头做,不受其他校区影响。")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|