| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- """Reconcile both sealed publications against the original inventory."""
- from collections import Counter
- from datetime import datetime,timezone
- import hashlib
- import json
- from pathlib import Path
- ROOT=Path(__file__).resolve().parents[1];WORK=ROOT/'work/library-cleanup-20261111'
- def load(p):return json.loads(p.read_bytes())
- def unwrap(v):return v['body']['result']
- before=unwrap(load(WORK/'inventory-result.json'))
- after=unwrap(load(WORK/'round-02/inventory-after-result.json'))
- plans=[load(d/'sealed-plan.json')['plan'] for d in [WORK,WORK/'round-02']]
- checks=[load(d/'verification-result.json') for d in [WORK,WORK/'round-02']]
- browser=load(WORK/'browser-final/report.json')
- assert all(c['ok'] for c in checks) and browser['ok']
- assert checks[-1]['generation']==after['generation']
- removals=[r for p in plans for r in p['removals']]
- assert len({(r['entity'],r['id']) for r in removals})==len(removals)
- def typed(row,entity,manifest):
- fields=next(t['columns'] for t in manifest['tables'] if t['name']==('social_posts' if entity=='social' else entity))
- result={k:v for k,v in row.items() if k not in ('_ksf_source_rowid','payload_sha256','source_payload')}
- for f in fields:
- if result.get(f['name']) is not None and 'INT' in f['type'].upper():result[f['name']]=int(result[f['name']])
- return result
- retained={}
- for entity in ['products','social']:
- removed={r['id'] for r in removals if r['entity']==entity}
- repairs={r['id']:r['changes'] for p in plans for r in p['repairs'] if r['entity']==entity and r['id'] not in removed}
- actual={r['id']:typed(r,entity,after['manifest']) for r in after[entity]}
- for raw in before[entity]:
- if raw['id'] in removed:assert raw['id'] not in actual;continue
- expected=typed(raw,entity,before['manifest']);expected.update(repairs.get(raw['id'],{}))
- assert expected==actual[raw['id']],raw['id']
- assert len(actual)==len(before[entity])-len(removed)
- retained[entity]=len(actual)
- social_removed={r['id'] for r in removals if r['entity']=='social'}
- retained_repairs={r['id'] for p in plans for r in p['repairs']}-social_removed
- for table in ['assets','source_records','source_links','source_origins','collection_requests','prices','policy_documents','policy_clauses','ranking_signals']:
- a=next(t for t in after['manifest']['tables'] if t['name']==table)
- b=next(t for t in before['manifest']['tables'] if t['name']==table)
- assert a==b,table
- receipt={'schema':'ksf.library-cleanup.final.v1','ok':True,'checkedAt':datetime.now(timezone.utc).isoformat(),
- 'baseGeneration':before['generation'],'generation':after['generation'],
- 'scannedProducts':len(before['products']),'scannedSocial':len(before['social']),
- 'removedByEntity':dict(Counter(r['entity'] for r in removals)),
- 'removedByReason':dict(Counter(r['reason'] for r in removals)),
- 'retainedFulltextCorrections':len(retained_repairs),'correctionOperations':sum(len(p['repairs']) for p in plans),
- 'correctedThenRemoved':sum(len(p['repairs']) for p in plans)-len(retained_repairs),
- 'protectedArticles':63,'retainedRecordsVerified':retained,'allRowsVerified':True,'browserVerified':True,'browserScenes':len(browser['scenes']),
- 'removedDependentReviews':150,'removedDependentSpecs':101,'sourceRecordsPreserved':20581,
- 'historicalGenerationsPreserved':True,'publicCounts':checks[-1]['counts'],
- 'limits':['本轮清理的是来源证据可确认的无关或无内容记录;未把访问失败、缺价格/图片/评论等同于无意义。',
- '有具体产品/配方/工艺参考价值但尚未取得正文的资料仍以仅标题线索保留,不能作为正文证据。']}
- (WORK/'final-receipt.json').write_text(json.dumps(receipt,ensure_ascii=False,indent=2),encoding='utf8')
- lines=['# 行业资料库清理验收','',f"核验时间:{receipt['checkedAt']}",'',
- '| 项目 | 清理前 | 清理后 | 本轮移除 |','|---|---:|---:|---:|',
- '| 商品与竞品 | 4,674 | 4,580 | 94 |','| 社媒内容 | 3,877 | 3,795 | 82 |',
- '| 关联电商评价 | 6,772 | 6,622 | 150 |','| 关联规格 | 5,737 | 5,636 | 101 |','',
- '商品清理:93条独立饮料、1条邮费链接。社媒清理:游戏/影视/歌曲/手工/明星片段及无有效内容的泛标题或挑战编号。',
- '当前保留资料中,1,258条正文标记已纠正为“仅标题线索”,不再把占位符、重复标题、纯链接和标签当正文。',
- '共执行1,272次正文标记纠正,其中14条在第二次复核后确认无意义并移除,以上统计不重复相加。','',
- '用户举例 bilibili:BV11ENX6BE6p 已移除;公开接口查询为0条,详情显示“未找到这条资料”。',
- '全量保留商品和社媒逐字段核对通过;两轮发布均全表SHA回读通过,关联外键无孤儿;63篇专项新资料完整保留。',
- '原始来源20,581条、来源文件7,450件、来源关联32,737条及历史代际保留;价格、政策和榜单原行未变。',
- '真实线上浏览器6场景通过:商品目录、移动社媒目录、删除社媒详情、删除商品详情、正文标记纠正、专项文章原文。未捕获异常0,横向溢出0。','',
- f"当前代际:`{after['generation']}`",'',*['- '+s for s in receipt['limits']]]
- (WORK/'清理结果.md').write_text('\n'.join(lines)+'\n',encoding='utf8')
- print(json.dumps({k:receipt[k] for k in ['ok','generation','removedByEntity','retainedFulltextCorrections','retainedRecordsVerified','browserScenes']},ensure_ascii=False))
|