# -*- coding: utf-8 -*-
from pathlib import Path
import csv, json, datetime
from openpyxl import Workbook, load_workbook
from openpyxl.styles import Alignment, Font, PatternFill
from openpyxl.utils import get_column_letter

project=Path('/Users/bot1/Volumes/root_for_ai/AI工作区/通用_法务合同台账_合同存档_20260607_1036')
deliv=project/'deliverables'
docs=project/'docs'
base_csv=deliv/'合同总表_20260607.csv'
base_xlsx=deliv/'合同总表_20260607.xlsx'
md_path=deliv/'合同总表_20260607.md'
analysis_path=deliv/'新增法务文档记录_20260607_第17-20份.md'
explain_path=deliv/'合同梳理说明_20260607.md'
catalog_csv=deliv/'可发送文件目录_20260607.csv'
catalog_md=deliv/'可发送文件目录_20260607.md'
manifest_path=docs/'source_files_manifest.json'
ocr_summary_path=docs/'ocr_summary.json'

cols=['序号','归档编号','签约/生效时间（OCR）','合同名称','合同类型','项目/IP','甲方','乙方','其他方/合作方','合约有效期/授权期限','授权/合作范围','金额/费用/分成','争议解决','风险/待核对','原始文件','OCR文本','SHA256','页数','OCR字符数']

def read_csv_rows(path):
    if not path.exists(): return []
    with path.open('r', encoding='utf-8-sig', newline='') as f:
        return list(csv.DictReader(f))

def write_csv_rows(path, rows, fieldnames):
    with path.open('w', encoding='utf-8-sig', newline='') as f:
        w=csv.DictWriter(f, fieldnames=fieldnames, extrasaction='ignore')
        w.writeheader()
        for r in rows:
            w.writerow({c:r.get(c,'') for c in fieldnames})

rows=read_csv_rows(base_csv)
norm=[]
for r in rows:
    rr={c:r.get(c,'') for c in cols}
    try: seq=int(str(rr.get('序号','')).strip())
    except Exception: seq=None
    if seq in range(17,21):
        continue
    norm.append(rr)

manifest=json.loads(manifest_path.read_text(encoding='utf-8'))
manifest_by_seq={int(m['seq']):m for m in manifest}
ocr_list=json.loads(ocr_summary_path.read_text(encoding='utf-8')) if ocr_summary_path.exists() else []
ocr_by_pdf={o.get('pdf'):o for o in ocr_list}

def meta(seq):
    m=manifest_by_seq[seq]
    pdf=Path(m['stored_pdf']).name
    return m, ocr_by_pdf.get(pdf,{})

def mk(seq, archive_id, signed, name, ctype, project_ip, party_a, party_b, others, term, scope, money, dispute, risk):
    m,o=meta(seq)
    return {
        '序号':str(seq), '归档编号':archive_id, '签约/生效时间（OCR）':signed, '合同名称':name,
        '合同类型':ctype, '项目/IP':project_ip, '甲方':party_a, '乙方':party_b, '其他方/合作方':others,
        '合约有效期/授权期限':term, '授权/合作范围':scope, '金额/费用/分成':money, '争议解决':dispute,
        '风险/待核对':risk, '原始文件':m['stored_pdf'], 'OCR文本':o.get('ocr_text',f'work/extracted_text_ocr/{Path(m["stored_pdf"]).stem}.txt'),
        'SHA256':m['sha256'], '页数':str(m['pages']), 'OCR字符数':str(o.get('chars',''))
    }

new_rows=[
    mk(17,'LC-20260607-017','授权期限：2026-05-09至2029-12-31（OCR识别，需结合主协议/盖章页复核）','授权证书（良渚文化/良渚博物院IP授权云汉寻真）','授权书/IP授权凭证','良渚文化、良渚博物院IP；云汉寻真品牌联名或展览招商','授权方/运营服务商：单页授权书OCR未清晰列明，需结合《品牌授权代理协议》及盖章主体复核','杭州云汉寻真文化创意有限公司','良渚文化、良渚博物院IP相关权利方/运营服务商','甲乙双方合作期限自2026-05-09至2029-12-31','授权素材包括IP图案、文字、logo、音视频、衍生形象、设计稿、视觉元素等；注册商标IP中国大陆（不含港澳台），著作权IP全球；普通许可，不含转授权；具体IP清单以后续提供为准','无直接金额条款；详细以《品牌授权代理协议》为准','授权书未单独约定；应以主协议争议解决为准','记录类文件。风险/待核对：单页授权书未完整显示授权方名称/盖章主体；授权内容依赖主协议及后续IP清单；logo/OCR误识别；需人工复核授权方、盖章、附件与主协议对应关系。'),
    mk(18,'LC-20260607-018','签字或盖章之日起生效；服务期限2026-01-20至2027-02-19（OCR识别，需核签章日期）','常年法律顾问服务合同（海蜂坤行银蜂套餐2026）','法务服务/常年法律顾问','万物有灵常年法务服务；海蜂坤行银蜂会员套餐','杭州万物有灵文化科技有限公司（OCR识别，需核对签章页）','海蜂坤行（深圳）网络科技有限公司','配合服务的律师事务所/专属法务团队（合同描述）','2026-01-20至2027-02-19；届满续约需另签书面合同','常年法律顾问、日常法律咨询、合同/函件审查、法律风险体检、劳动用工/合同管理制度、律师函及诉讼咨询等；含法务管理系统使用/维护','服务费人民币29,800元/年；签订后3个工作日内支付；具体银行账号未写入台账，原件留存备查','深圳国际仲裁委员会仲裁','记录并提示：服务额度/响应时效/重要合同25份等配额需跟踪；专项诉讼、商标打假、重大非诉需另行收费；乙方并非法院诉讼代理主体时由合作律所配合；联系人/账号/开票信息等敏感字段未写入台账。'),
    mk(19,'LC-20260607-019','协议编号2026011501；采购订单日期2026-01-15；主协议自双方签字/盖章生效（签署页日期需人工复核）','文化产品合作协议及采购订单（万物有灵/四川极深奇乐2026）','产品合作/采购订单/IP文创产品开发','中国国家博物馆/国博衍艺相关文创衍生品；国宝器韵发卡/发圈等','杭州万物有灵文化科技有限公司','四川极深奇乐文化传播有限公司','国博方/甲方客户；国博（北京）文化产业发展有限公司、中国国家博物馆相关限制表述','主协议合作期限2年，自签订之日至2027-12-31；采购订单为主合同附件','甲方提供中国国家博物馆馆藏图片等素材及设计需求；乙方负责产品开发、设计、宣传、生产制作、运输、售后并承担费用；未经甲方书面同意不得向第三方销售/赠送合作产品','采购订单合计约人民币40,740.00元（含设计/打样/包装/产品/13%专票/运费等）；乙方开票且甲方收到国博方对应款项后30日内结算；收付款账号未写入台账，原件留存备查','甲方所在地有管辖权人民法院','重点合同。风险/待核对：乙方知识产权/产品质量/私售责任很重，含100万元名誉损失费及私售2倍著作权使用费；成果知识产权归甲方或指定第三方；国博方未付款时甲方仅积极协调，乙方回款存在上游付款风险；收货15日异议期但隐蔽瑕疵除外；采购订单包含收件电话/账户等敏感信息，未写入台账。'),
    mk(20,'LC-20260607-020','协议编号20260114；签署时间2026-01-14（OCR识别）','保密协议（上海景行/万物有灵产品开发）','保密协议/NDA/产品开发保密','万物有灵产品开发、图库/VI/打样产品/客户及商业信息保密','杭州万物有灵文化科技有限公司','上海景行手信文化发展有限公司','甲方关联企业、甲方客户/潜在客户（保密信息涉及）','自签署日起生效；保密义务在服务合同有效期内及终止/解除后一直有效，直至甲方宣布解密或秘密信息合法公开','乙方因服务甲方接触的图库、技术、财务、商业资讯、VI设计、打样产品、客户名单、报价、产品开发计划、商业策略等均属保密范围；未经书面同意不得披露/使用/转让等','无服务费条款；违约金5万元，不足以弥补损失的继续补足；维权成本由违约方承担','甲方所在地人民法院','记录类NDA。风险/待核对：条款编号存在跳号（2.4缺失）；保密范围较宽但对乙方例外披露/返还销毁/员工约束细节不够细；违约金5万元可能不足覆盖重大商业秘密泄露；签章信息需人工复核。'),
]

all_rows=norm+new_rows
all_rows.sort(key=lambda r:int(str(r.get('序号','0')).strip() or 0))
write_csv_rows(base_csv, all_rows, cols)

src_cols=['序号','展示文件名','原始缓存文件名','存档PDF','原生文本','OCR文本','SHA256','页数','原生文本字符数','OCR字符数','添加时间']
src_rows=[]
for m in sorted(manifest, key=lambda x:int(x.get('seq',0))):
    pdf=Path(m.get('stored_pdf','')).name
    o=ocr_by_pdf.get(pdf,{})
    src_rows.append({'序号':m.get('seq'),'展示文件名':m.get('display_name'),'原始缓存文件名':m.get('original'),'存档PDF':m.get('stored_pdf'),'原生文本':m.get('stored_text'),'OCR文本':o.get('ocr_text',''),'SHA256':m.get('sha256'),'页数':m.get('pages'),'原生文本字符数':m.get('chars'),'OCR字符数':o.get('chars',''),'添加时间':m.get('added_at')})

# Sendable file catalog. Existing rows get a coarse category from ledger type/name if not manually classified.
def category_for(r):
    name=(r.get('合同名称','')+' '+r.get('合同类型','')+' '+r.get('项目/IP','')).lower()
    if '授权证书' in name or '授权书' in name: return '授权书/IP授权凭证'
    if '保密' in name or 'nda' in name: return '保密协议/NDA'
    if '法律顾问' in name or '法务服务' in name: return '法务服务合同'
    if '采购' in name or '产品合作' in name or '文化产品' in name: return '产品合作/采购订单'
    if '屏销宝' in name or '电视淘宝' in name: return '广告投放/技术服务'
    if '品牌/ip授权代理' in name or '授权代理' in name: return '品牌/IP授权代理合同'
    return '合同/协议'

catalog_cols=['序号','归档编号','文件类别','可检索关键词','建议调用场景','展示文件名','存档PDF','合同名称','甲方','乙方','有效期/授权期','SHA256']
manifest_by_rel={m['stored_pdf']:m for m in manifest}
catalog=[]
for r in all_rows:
    m=manifest_by_rel.get(r.get('原始文件',''),{})
    cat=category_for(r)
    keywords='、'.join([x for x in [r.get('合同名称',''), r.get('项目/IP',''), r.get('甲方',''), r.get('乙方',''), cat] if x])
    scene={
        '授权书/IP授权凭证':'对外证明IP授权、品牌联名/展览招商授权、查授权期限/范围时调用',
        '保密协议/NDA':'对外合作前保密约束、产品开发资料披露前、追责泄密时调用',
        '法务服务合同':'查询法务服务期限、服务额度、顾问服务范围、仲裁条款时调用',
        '产品合作/采购订单':'查供应商、采购金额、交付/结算、产品质量/IP责任时调用',
        '广告投放/技术服务':'查电视淘宝屏销宝年框投放、金额、曝光量、付款与风险时调用',
        '品牌/IP授权代理合同':'查IP授权/代理范围、期限、分成、招商合作时调用',
    }.get(cat,'需要调取该合同/协议原件或核对条款时调用')
    catalog.append({'序号':r.get('序号',''),'归档编号':r.get('归档编号',''),'文件类别':cat,'可检索关键词':keywords[:500],'建议调用场景':scene,'展示文件名':m.get('display_name',''),'存档PDF':r.get('原始文件',''),'合同名称':r.get('合同名称',''),'甲方':r.get('甲方',''),'乙方':r.get('乙方',''),'有效期/授权期':r.get('合约有效期/授权期限',''),'SHA256':r.get('SHA256','')})
write_csv_rows(catalog_csv, catalog, catalog_cols)

def write_md_table(path, rows, fieldnames, title, note=''):
    md=[f'# {title}\n']
    if note: md.append(f'> {note}\n')
    md.append('| ' + ' | '.join(fieldnames) + ' |')
    md.append('| ' + ' | '.join(['---']*len(fieldnames)) + ' |')
    def esc(x): return ('' if x is None else str(x)).replace('|','｜').replace('\n','<br>')
    for r in rows:
        md.append('| ' + ' | '.join(esc(r.get(c,'')) for c in fieldnames) + ' |')
    path.write_text('\n'.join(md)+'\n', encoding='utf-8')

write_md_table(md_path, all_rows, cols, '合同总表（截至2026-06-07）', '本表根据PDF文本层及OCR整理；扫描件中的签署日期、印章、手写内容、金额/表格均需以原件人工复核为准。银行账号、电话、税号等敏感信息不在本表列示。')
write_md_table(catalog_md, catalog, catalog_cols, '可发送文件目录（截至2026-06-07）', '用于后续按关键词/类别快速找到原件并发送。若用户重复发送同一文件，按SHA256去重并提醒。')

field_desc=[
    {'字段':'签约/生效时间（OCR）','说明':'根据PDF文本层/OCR识别；签署页、盖章页、手写日期一律需人工复核。'},
    {'字段':'合约有效期/授权期限','说明':'记录合同期限、代理/授权期限、续约或终止规则；如合同仅约定生效条件则说明。'},
    {'字段':'金额/费用/分成','说明':'仅摘录主交易口径；银行账号、税号等敏感信息不进入总表。'},
    {'字段':'风险/待核对','说明':'法务辅助风险提示，不替代执业律师意见；高风险合同建议律师最终确认。'},
    {'字段':'SHA256','说明':'用于校验原始PDF未被篡改或重复上传。'},
    {'字段':'可发送文件目录','说明':'按文件类别、关键词、调用场景维护，便于后续直接发送原件。'},
]
now=datetime.datetime.now().isoformat(timespec='seconds')
process_rows=[
    {'时间':now,'事项':'追加处理第17-20份法务文档','说明':'新增授权书、法务服务合同、文化产品合作协议/采购订单、保密协议；均已OCR并分类记录。'},
    {'时间':now,'事项':'去重策略','说明':'按PDF SHA256去重；本批4份未发现与既有20份重复。后续重复上传会提示已有归档编号与路径。'},
    {'时间':now,'事项':'可发送目录','说明':'新增/更新可发送文件目录，按授权书、NDA、法务服务、产品合作等类别归类。'},
    {'时间':now,'事项':'敏感信息处理','说明':'台账和目录不列银行账号、联系人电话、税号等敏感信息；原始PDF/OCR文本留存备查。'},
]

def add_sheet(wb, title, rows, fieldnames):
    ws=wb.create_sheet(title)
    ws.append(fieldnames)
    for r in rows:
        ws.append([r.get(c,'') for c in fieldnames])
    return ws

wb=Workbook(); wb.remove(wb.active)
add_sheet(wb,'合同总表',all_rows,cols)
add_sheet(wb,'源文件清单',src_rows,src_cols)
add_sheet(wb,'可发送文件目录',catalog,catalog_cols)
add_sheet(wb,'字段说明',field_desc,['字段','说明'])
add_sheet(wb,'处理说明',process_rows,['时间','事项','说明'])
for ws in wb.worksheets:
    ws.freeze_panes='A2'
    for cell in ws[1]:
        cell.font=Font(bold=True, color='FFFFFF')
        cell.fill=PatternFill('solid', fgColor='4F81BD')
        cell.alignment=Alignment(horizontal='center', vertical='center', wrap_text=True)
    for row_cells in ws.iter_rows(min_row=2):
        for cell in row_cells:
            cell.alignment=Alignment(vertical='top', wrap_text=True)
    for col in range(1, ws.max_column+1):
        letter=get_column_letter(col)
        max_len=0
        for cell in ws[letter]:
            val='' if cell.value is None else str(cell.value)
            max_len=max(max_len, min(len(val), 60))
        ws.column_dimensions[letter].width=max(10, min(max_len+2, 45))
    ws.auto_filter.ref=ws.dimensions
wb.save(base_xlsx)

analysis=f'''# 新增法务文档记录（第17-20份）

处理时间：{now}

> 说明：本批按“后续需要时可直接发送”的目标处理：保留原始PDF、OCR文本、SHA256，并新增/更新“可发送文件目录”。本摘要不列银行账号、税号、电话等敏感字段；签章、手写日期、印章主体以原件人工复核为准。

## 本批归类

| 编号 | 文件 | 归类 | 关键用途 |
|---|---|---|---|
| LC-20260607-017 | 良渚云汉寻真授权书 | 授权书/IP授权凭证 | 证明良渚文化/良渚博物院IP对云汉寻真的品牌联名或展览招商授权；授权期2026-05-09至2029-12-31 |
| LC-20260607-018 | 海蜂法务服务合同2026 | 法务服务合同/常年法律顾问 | 查询海蜂坤行银蜂套餐、服务期限2026-01-20至2027-02-19、服务额度、仲裁条款 |
| LC-20260607-019 | 万物有灵/四川极深奇乐产品合作协议2026 | 产品合作/采购订单/IP文创产品开发 | 查询国博文创产品供应、采购金额约40,740元、交付、结算、质量/IP责任 |
| LC-20260607-020 | 上海景行/万物有灵产品开发保密协议 | 保密协议/NDA | 产品开发前保密约束；签署时间2026-01-14；保密义务持续至解密或合法公开 |

## 去重结果

本批4份均为新增，未发现与既有归档文件SHA256重复。后续如重复发送同一PDF，将按SHA256提醒已有归档编号与存档路径，不重复入账。

## 简要风险/复核提示

1. **良渚授权书**：单页授权证书未完整显示授权方/盖章主体；需结合《品牌授权代理协议》、盖章页和后续IP清单复核。
2. **海蜂法务服务合同**：注意服务额度、响应时效、重要合同25份等配额；专项诉讼、商标打假、重大非诉需另行收费；争议由深圳国际仲裁委员会处理。
3. **四川极深奇乐产品合作协议**：产品质量/IP/私售责任较重；采购结算与国博方付款挂钩，需关注回款节点；采购订单含收件信息和账户信息，已仅在原件/OCR留存。
4. **上海景行保密协议**：保密范围较宽，违约金5万元；建议后续重要项目增加资料返还/销毁、人员访问限制、例外披露流程等细节。
'''
analysis_path.write_text(analysis, encoding='utf-8')

marker='## 2026-06-07 追加处理：第17-20份法务文档'
old=explain_path.read_text(encoding='utf-8') if explain_path.exists() else '# 合同梳理说明\n'
append=f'''\n\n{marker}\n\n- 新增并处理4份文档：授权书、法务服务合同、产品合作协议/采购订单、保密协议。\n- 已按用户要求自行归类，并新增/更新 `deliverables/可发送文件目录_20260607.csv` 与 `deliverables/可发送文件目录_20260607.md`，Excel中也新增 `可发送文件目录` sheet，便于后续直接调取/发送原件。\n- 本批未发现SHA256重复；后续重复上传将提示已有归档编号和路径，不重复入账。\n- 台账/目录不列银行账号、联系人电话、税号等敏感字段，原件与OCR文本留存备查。\n'''
if marker in old:
    old=old[:old.index(marker)].rstrip()+append
else:
    old=old.rstrip()+append
explain_path.write_text(old+'\n',encoding='utf-8')

readme=project/'README.md'
rt=readme.read_text(encoding='utf-8') if readme.exists() else '# 法务合同台账与合同存档\n'
line='- 当前台账：截至2026-06-07已整理20份合同/法务文档；含合同、授权书、NDA、法务服务、产品合作/采购订单；可按“可发送文件目录”快速调取原件。'
if '- 当前台账：' in rt:
    rt='\n'.join([line if l.startswith('- 当前台账：') else l for l in rt.splitlines()])+'\n'
else:
    rt=rt.rstrip()+'\n'+line+'\n'
readme.write_text(rt,encoding='utf-8')

print('UPDATED_17_20')
print('rows', len(all_rows))
print('source_manifest_count', len(manifest))
print('ocr_summary_count', len(ocr_list))
print('xlsx', base_xlsx, base_xlsx.stat().st_size)
print('csv', base_csv, base_csv.stat().st_size)
print('catalog_csv', catalog_csv, catalog_csv.stat().st_size)
print('analysis', analysis_path, analysis_path.stat().st_size)
for r in all_rows[-4:]:
    print(r['序号'], r['归档编号'], r['合同类型'], r['合同名称'])
