공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
97 lines
5.5 KiB
Python
97 lines
5.5 KiB
Python
# -*- 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:]))
|