# -*- coding: utf-8 -*- """김제시 후처리 2규칙: R1) F가 병합(같은 D,E,F 연속 ≥2행)인데 그 블록 '첫 행'의 G가 비어있으면 그 행 삭제(부모 랜딩 셸). R2) F 또는 G 텍스트 끝이 '새창열림'이면 → L=사이트, M·N·O·P·Q 삭제, 텍스트에서 '새창열림' 제거. 사용: python -X utf8 _김제_정리.py plan | run """ import os, sys, re, io, shutil, importlib.util, warnings warnings.filterwarnings('ignore') sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', line_buffering=True) import openpyxl HERE = os.path.dirname(os.path.abspath(__file__)) def _imp(n, p): s = importlib.util.spec_from_file_location(n, p); m = importlib.util.module_from_spec(s); s.loader.exec_module(m); return m dd = _imp('dd', os.path.join(HERE, '_스크립트', '_dedup_all.py')) XP = os.path.join(HERE, '작업파일', '광역_사이트맵', '전북특별자치도', '3.김제시', '전북특별자치도_김제시.xlsx') NEWWIN = re.compile(r'\s*새\s*창\s*열림\s*$') def lab(v): return ' > '.join(str(v.get(c)) for c in range(4, 11) if v.get(c) not in (None, '')) def main(): mode = sys.argv[1] if len(sys.argv) > 1 else 'plan' wb = openpyxl.load_workbook(XP); ws = wb.active rows = dd.load_flat(ws) # --- R2: 새창열림 — 행의 leaf(가장 깊은 카테고리) 라벨 끝이 새창열림이면 사이트화 --- r2 = [] for r in rows: v = r['vals'] lc = dd.leaf_depth(v) # 가장 깊은 카테고리 컬럼(E/F/G…) t = v.get(lc) if isinstance(t, str) and NEWWIN.search(t): r2.append(r) # 적용: 사이트화 + M/N/O/P/Q 삭제. 텍스트 strip 은 전체 행·전 카테고리 컬럼에서. for r in r2: v = r['vals'] r['_had_pq'] = any(v.get(c) not in (None, '') for c in (16, 17)) v[12] = '사이트' # L for c in (13, 14, 15, 16, 17): # M N O P Q v[c] = None # 새창열림 텍스트 제거(모든 행, D~J) for r in rows: v = r['vals'] for c in range(4, 11): t = v.get(c) if isinstance(t, str) and NEWWIN.search(t): v[c] = NEWWIN.sub('', t).strip() # --- R1: F-block 첫행 G 비면 삭제 --- # F-block = 같은 (D,E,F) 연속 run, F 비어있지 않음, run길이>=2 delete = set() # id(row) n = len(rows) i = 0 while i < n: v = rows[i]['vals'] F = v.get(6) if F in (None, ''): i += 1; continue j = i while j + 1 < n and rows[j+1]['vals'].get(6) == F \ and rows[j+1]['vals'].get(4) == v.get(4) \ and rows[j+1]['vals'].get(5) == v.get(5): j += 1 runlen = j - i + 1 if runlen >= 2 and rows[i]['vals'].get(7) in (None, ''): delete.add(id(rows[i])) i = j + 1 del_rows = [r for r in rows if id(r) in delete] keep = [r for r in rows if id(r) not in delete] print('=== 김제 정리 plan ===') print(f'기존행 {len(rows)}') print(f'\n[R2] 새창열림 → 사이트: {len(r2)}건 (그중 P/Q 보유 {sum(1 for r in r2 if r.get("_had_pq"))}건)') for r in r2[:40]: print(f' r{r["src"]}: {lab(r["vals"])}') print(f'\n[R1] F병합 첫행 G빈칸 → 삭제: {len(del_rows)}건') for r in del_rows[:60]: print(f' r{r["src"]}: {lab(r["vals"])} (K={str(r["vals"].get(11))[-40:]})') print(f'\n결과행: {len(rows)} → {len(keep)}') if mode != 'run': return bak = XP.replace('.xlsx', '_backup_정리전.xlsx') shutil.copy(XP, bak) dd.write_back(ws, keep) try: wb.save(XP); print(f'\n저장완료 {len(rows)}→{len(keep)}행. 백업 {os.path.basename(bak)}') except PermissionError: alt = XP.replace('.xlsx', '_LP.xlsx'); wb.save(alt); print(f'\n!! 잠김 → {os.path.basename(alt)}') if __name__ == '__main__': main()