# -*- coding: utf-8 -*- """_audit_*.json 들을 읽어 신뢰도별 재검수 diff 리포트(MD) 생성. 읽기전용.""" import json, os, io, glob HERE = os.path.dirname(os.path.abspath(__file__)) TEMP = os.path.join(HERE, '..', '_temp') TARGETS_ORDER = [ ('충남', ['서산시','아산시','천안시','청양군','홍성군']), ('충북', ['괴산군','단양군','보은군','영동군','옥천군','음성군','제천시','증평군','진천군','청주시','충주시']), ('전북', ['부안군','순창군','완주군','익산시','임실군','장수군','전주시','정읍시','진안군']), ('제주', ['서귀포시','제주시']), ] def load(name): p = os.path.join(TEMP, f'_audit_{name}.json') return json.load(open(p, encoding='utf-8')) if os.path.exists(p) else None def classify_L(x): o, n, nm, om = x['oldL'], x['newL'], (x['newM'] or 0), (x['oldM'] or 0) try: nm = int(nm) except: nm = 0 try: om = int(om) except: om = 0 if o == '페이지' and n == '게시판': return 'HIGH_board' if nm >= 3 else 'noise_searchbox' # M=0/소수 = 검색창 누출 의심 if o == '게시판' and n == '페이지': return 'demote_empty' if om <= 1 else 'demote_suspect' # oldM 큰데 페이지화=일시오류 의심 return 'other' def classify_N(x): o, n = (x['oldN'] or ''), x['newN'] so, sn = set(o.split(',')) if o else set(), set(n.split(',')) if n else set() if n == '없음': return 'to_none' if '이미지' in sn and '이미지' not in so: return 'add_image' if '이미지' in so and '이미지' not in sn: return 'drop_image' if ('영상' in sn) != ('영상' in so): return 'video' if ('오디오' in sn) != ('오디오' in so): return 'audio' return 'other' lines = ['# 미검수 27곳 재검수 audit 리포트 (읽기전용 · xlsx 무수정)', '', '> 현행규칙(매뉴얼 2-1 L판정 · 3-1a/3-3/3-4b N판정)으로 K열 URL 재접속·재분류 → 기존값 diff.', ''] grand = {'HIGH_board':0,'noise_searchbox':0,'demote_empty':0,'demote_suspect':0, 'to_none':0,'add_image':0,'drop_image':0,'video':0,'audio':0} for reg, names in TARGETS_ORDER: lines.append(f'## {reg}') for nm in names: d = load(nm) if not d: lines.append(f'- **{nm}**: (결과없음)'); continue if d.get('error'): lines.append(f'- **{nm}**: {d["error"]}'); continue Lb = {'HIGH_board':[],'noise_searchbox':[],'demote_empty':[],'demote_suspect':[],'other':[]} for x in d['L_diffs']: Lb[classify_L(x)].append(x) Nb = {'to_none':[],'add_image':[],'drop_image':[],'video':[],'audio':[],'other':[]} for x in d['N_diffs']: Nb[classify_N(x)].append(x) for k in grand: grand[k] += len(Lb.get(k,[])) + len(Nb.get(k,[])) flags = [] if Lb['HIGH_board']: flags.append(f"🟥게시판누락 {len(Lb['HIGH_board'])}") if Lb['demote_empty']: flags.append(f"🟧빈게시판→페이지 {len(Lb['demote_empty'])}") if Nb['add_image']: flags.append(f"🟦이미지추가 {len(Nb['add_image'])}") if Nb['drop_image']: flags.append(f"🟦이미지강등 {len(Nb['drop_image'])}") if Nb['to_none']: flags.append(f"⬜빈게시판→없음 {len(Nb['to_none'])}") if Nb['video']+Nb['audio']: flags.append(f"🎬영상/오디오 {len(Nb['video'])+len(Nb['audio'])}") noise = len(Lb['noise_searchbox']) + len(Lb['demote_suspect']) head = f"- **{nm}** (행{d['rows']}, 실패{d['fails']}): " + (', '.join(flags) if flags else '변경 없음 ✅') if noise: head += f" _(노이즈억제: 검색창의심 {len(Lb['noise_searchbox'])}+오분류의심강등 {len(Lb['demote_suspect'])})_" lines.append(head) # 상세: HIGH_board, demote_empty, image, none, video/audio (각 최대 12) def detail(items, fmt): for x in items[:12]: lines.append(' - ' + fmt(x)) if len(items) > 12: lines.append(f' - …외 {len(items)-12}건') detail(Lb['HIGH_board'], lambda x: f"[게시판누락] r{x['r']} {(x['label'] or '')[:22]} 페이지→게시판 M{x['oldM']}→{x['newM']} {x['url'][:75]}") detail(Lb['demote_empty'], lambda x: f"[빈게시판] r{x['r']} {(x['label'] or '')[:22]} 게시판→페이지 M{x['oldM']}→{x['newM']}") detail(Nb['add_image'], lambda x: f"[이미지+] r{x['r']} {(x['label'] or '')[:22]} '{x['oldN']}'→'{x['newN']}' ({x['L']})") detail(Nb['drop_image'], lambda x: f"[이미지-] r{x['r']} {(x['label'] or '')[:22]} '{x['oldN']}'→'{x['newN']}'") detail(Nb['to_none'], lambda x: f"[없음] r{x['r']} {(x['label'] or '')[:22]} '{x['oldN']}'→없음 ({x['L']})") detail(Nb['video']+Nb['audio'], lambda x: f"[AV] r{x['r']} {(x['label'] or '')[:22]} '{x['oldN']}'→'{x['newN']}'") lines.append('') lines.append('## 합계 (신뢰 분류)') lines.append(f"- 🟥 게시판 누락(페이지→실게시판 M≥3): **{grand['HIGH_board']}**") lines.append(f"- 🟧 빈게시판→페이지(oldM≤1): **{grand['demote_empty']}**") lines.append(f"- 🟦 이미지 추가: **{grand['add_image']}** / 이미지 강등: **{grand['drop_image']}**") lines.append(f"- ⬜ 빈게시판→없음: **{grand['to_none']}**") lines.append(f"- 🎬 영상/오디오: **{grand['video']+grand['audio']}**") out = os.path.join(TEMP, '_재검수_리포트.md') io.open(out, 'w', encoding='utf-8').write('\n'.join(lines)) print('WROTE', out) print('\n'.join(lines[-7:]))