# -*- coding: utf-8 -*- """랜딩셸 삭제(1-5e): menu?menuKey= 카테고리 인덱스(병합 첫행·다음깊이 빈칸·자식 보유) 제거. 풀리빌드(효과경로+스타일 보존 → 재기록 → 동일값런 재병합 → K링크 → B재부번).""" import openpyxl, copy, re, io, sys from openpyxl.utils import get_column_letter from openpyxl.worksheet.hyperlink import Hyperlink sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') PATH = '한국청소년정책연구원.xlsx' wb = openpyxl.load_workbook(PATH); ws = wb.active merges = list(ws.merged_cells.ranges) def eff(r, c): v = ws.cell(r, c).value if v is not None: return v for m in merges: if m.min_row <= r <= m.max_row and m.min_col <= c <= m.max_col: return ws.cell(m.min_row, m.min_col).value return None def mtop(r, c): for m in merges: if m.min_row <= r <= m.max_row and m.min_col <= c <= m.max_col: return (m.min_row, m.min_col) return (r, c) def deepest(path): last = -1 for i in range(7): if path[i] not in (None, ''): last = i return last COLS = range(2, 28) DATA0 = ws.max_row rows = [] for r in range(3, DATA0 + 1): path = [eff(r, c) for c in range(4, 11)] # D..J rows.append({'r': r, 'path': path, 'vals': {c: ws.cell(r, c).value for c in COLS}, 'klink': (ws.cell(r, 11).hyperlink.target if ws.cell(r, 11).hyperlink else None), 'styles': {c: copy.copy(ws.cell(*mtop(r, c))._style) for c in COLS}}) MENU = re.compile(r'/menu\?menuKey=') # 셸 판정: K가 menu-index AND 다음행이 같은 상위경로+더 깊은 깊이(자식 보유) shells = set() for i, rw in enumerate(rows): k = rw['vals'].get(11) or '' if not MENU.search(k): continue c = deepest(rw['path']) if c < 0: continue # 자기 다음깊이(c+1)는 비어야 함(랜딩) if c + 1 < 7 and rw['path'][c + 1] not in (None, ''): continue # 뒤쪽에 같은 [0..c] 경로 + c+1 채워진 자식이 있나 haschild = False for j in range(i + 1, len(rows)): op = rows[j]['path'] if op[:c + 1] != rw['path'][:c + 1]: break # 상위경로 벗어남 if op[c + 1] not in (None, ''): haschild = True; break if haschild: shells.add(rw['r']) print('삭제 셸 행:', sorted(shells)) for r in sorted(shells): rw = next(x for x in rows if x['r'] == r) print(' r%d %s | %s' % (r, ' > '.join([p for p in rw['path'] if p]), rw['vals'].get(11))) OUT = [rw for rw in rows if rw['r'] not in shells] # 재기록 for m in list(ws.merged_cells.ranges): if m.min_row >= 3: ws.unmerge_cells(str(m)) for r in range(3, DATA0 + 1): for c in COLS: ws.cell(r, c).value = None; ws.cell(r, c).hyperlink = None for i, rw in enumerate(OUT): rr = 3 + i for k in range(7): ws.cell(rr, 4 + k).value = rw['path'][k] ws.cell(rr, 2).value = i + 1 for c in range(3, 28): if c < 4 or c > 10: ws.cell(rr, c).value = rw['vals'].get(c) ws.cell(rr, 3).value = rw['vals'].get(3) if rw['klink']: ws.cell(rr, 11).hyperlink = Hyperlink(ref='K%d' % rr, target=rw['klink']) for c in COLS: ws.cell(rr, c)._style = copy.copy(rw['styles'][c]) NROW = 2 + len(OUT) # 동일값런 재병합 (D..J) for c in range(4, 11): r = 3 while r <= NROW: v = ws.cell(r, c).value if v in (None, ''): r += 1; continue key = tuple(ws.cell(r, pc).value for pc in range(4, c + 1)) r2 = r while r2 + 1 <= NROW and tuple(ws.cell(r2 + 1, pc).value for pc in range(4, c + 1)) == key: r2 += 1 if r2 > r: for rr in range(r + 1, r2 + 1): ws.cell(rr, c).value = None ws.merge_cells(start_row=r, start_column=c, end_row=r2, end_column=c) r = r2 + 1 wb.save(PATH) print('삭제 %d행 → 최종 %d행' % (len(shells), len(OUT)))