# -*- coding: utf-8 -*- """외부 URL인데 사이트 아님 정리 (충남/충북/제주, 검수완료 제외). - 외부도메인(기관 core 도메인이 host에 없음) & L!=사이트 인 행: · D/E/F/G 라벨 전부 빈칸 → 템플릿 잔재 → 삭제 · 라벨 있음 → 진짜 외부링크 → L=사이트, M/N/O/P/Q 비움, S=외부링크 flatten→잔재제외+사이트적용→재작성→D/E/F 재병합→K하이퍼링크. """ import sys, glob, os, re, shutil, warnings from copy import copy from urllib.parse import urlsplit from collections import Counter import openpyxl from openpyxl.styles import Alignment, Font warnings.filterwarnings('ignore') MAXCOL = 27 HEADER_MERGES = {'B1:R1', 'S1:W1', 'Y1:AA1'} EXCLUDE = {'계룡시', '공주시', '금산군', '논산시', '보령시'} def host_of(u): try: return urlsplit(u).netloc.lower() except Exception: return '' def process(path, write): wb = openpyxl.load_workbook(path) ws = wb.active # flatten cat for label check + main domain fcat = {} for r in range(3, ws.max_row + 1): fcat[r] = {c: ws.cell(r, c).value for c in (4, 5, 6, 7)} for mr in ws.merged_cells.ranges: if mr.min_col in (4, 5, 6) and mr.min_row >= 3: top = ws.cell(mr.min_row, mr.min_col).value for rr in range(mr.min_row, mr.max_row + 1): if rr in fcat and fcat[rr].get(mr.min_col) in (None, ''): fcat[rr][mr.min_col] = top hosts = Counter() for r in range(3, ws.max_row + 1): if ws.cell(r, 2).value: k = ws.cell(r, 11).value if isinstance(k, str) and k.startswith('http'): hosts[host_of(k)] += 1 if not hosts: return (0, 0) core = re.sub(r'^www\.', '', hosts.most_common(1)[0][0]).split('.')[0] delete = set() sitemark = set() for r in range(3, ws.max_row + 1): if not ws.cell(r, 2).value: continue k = ws.cell(r, 11).value L = ws.cell(r, 12).value if isinstance(k, str) and k.startswith('http'): h = host_of(k) if core and core not in h and L != '사이트': haslabel = any(fcat[r].get(c) not in (None, '') for c in (4, 5, 6, 7)) if haslabel: sitemark.add(r) else: delete.add(r) if not delete and not sitemark: return (0, 0) if not write: return (len(delete), len(sitemark)) shutil.copy(path, path.replace('.xlsx', '_backup_외부정리전.xlsx')) # flatten for mr in list(ws.merged_cells.ranges): s = str(mr) if s in HEADER_MERGES: continue top = ws.cell(mr.min_row, mr.min_col).value ws.unmerge_cells(s) for rr in range(mr.min_row, mr.max_row + 1): for cc in range(mr.min_col, mr.max_col + 1): if ws.cell(rr, cc).value in (None, ''): ws.cell(rr, cc).value = top out = [] for r in range(3, ws.max_row + 1): if all(ws.cell(r, c).value in (None, '') for c in range(2, MAXCOL + 1)): continue if r in delete: continue vals = {c: ws.cell(r, c).value for c in range(1, MAXCOL + 1)} if r in sitemark: vals[12] = '사이트' # L vals[13] = None # M vals[14] = None # N vals[15] = None # O vals[16] = None # P vals[17] = None # Q vals[19] = '외부링크' # S styles = {c: (copy(ws.cell(r, c).font), copy(ws.cell(r, c).fill), copy(ws.cell(r, c).border), copy(ws.cell(r, c).alignment), ws.cell(r, c).number_format, copy(ws.cell(r, c).protection)) for c in range(1, MAXCOL + 1)} out.append({'vals': vals, 'styles': styles}) for r in range(3, ws.max_row + 1): for c in range(1, MAXCOL + 1): ws.cell(r, c).value = None ws.cell(r, c).hyperlink = None START = 3 for i, orow in enumerate(out): rr = START + i for c in range(1, MAXCOL + 1): f, fl, bd, al, nf, pr = orow['styles'][c] cell = ws.cell(rr, c) cell.font = copy(f); cell.fill = copy(fl); cell.border = copy(bd) cell.alignment = copy(al); cell.number_format = nf; cell.protection = copy(pr) cell.value = orow['vals'].get(c) ws.cell(rr, 2).value = i + 1 END = START + len(out) - 1 for rr in range(END + 1, ws.max_row + 1): for c in range(1, MAXCOL + 1): ws.cell(rr, c).value = None ws.cell(rr, c).hyperlink = None center = Alignment(horizontal='center', vertical='center', wrap_text=True) left = Alignment(horizontal='left', vertical='center', wrap_text=False) def merge_runs(letter, idx, gc=()): cur = ws.cell(START, idx).value grp = tuple(ws.cell(START, g).value for g in gc) st = START; runs = [] for rr in range(START + 1, END + 1): val = ws.cell(rr, idx).value g2 = tuple(ws.cell(rr, gg).value for gg in gc) if val == cur and g2 == grp: continue if cur not in (None, '') and rr - 1 > st: runs.append((st, rr - 1)) cur, grp, st = val, g2, rr if cur not in (None, '') and END > st: runs.append((st, END)) for s, e in runs: ws.merge_cells(f'{letter}{s}:{letter}{e}') ws.cell(s, idx).alignment = center merge_runs('F', 6, (4, 5)) merge_runs('E', 5, (4,)) merge_runs('D', 4) for rr in range(START, END + 1): for c in (4, 5, 6, 7): if ws.cell(rr, c).value is not None: ws.cell(rr, c).alignment = center for rr in range(1, END + 1): ws.row_dimensions[rr].height = 15 for rr in range(START, END + 1): cell = ws.cell(rr, 11) u = cell.value if isinstance(u, str) and u.startswith(('http://', 'https://')): cell.hyperlink = u old = cell.font cell.font = Font(name=old.name or '맑은 고딕', size=old.size or 11, bold=old.bold, italic=old.italic, color='0000FF', underline='single') cell.alignment = left wb.save(path) return (len(delete), len(sitemark)) def main(): write = '--write' in sys.argv regions = ['충청남도', '충청북도', '제주특별자치도'] base = os.path.dirname(os.path.abspath(__file__)) for region in regions: for path in sorted(glob.glob(os.path.join(base, region, '*', '*.xlsx'))): if 'backup' in path or '_r2' in path: continue nm = os.path.basename(path).replace('.xlsx', '') if nm in EXCLUDE: continue d, s = process(path, write) if d or s: tag = '저장' if write else 'dry' print(f'[{tag}] {nm}: 삭제(잔재) {d} / 사이트표시 {s}') if __name__ == '__main__': main()