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