# -*- coding: utf-8 -*- import openpyxl, json, sys, re from copy import copy from openpyxl.utils import get_column_letter sys.stdout.reconfigure(encoding='utf-8') SRC = r'충청남도_서천군.xlsx' OUT = r'_충청남도_서천군_NEW.xlsx' # temp; swap after verify plan = json.load(open('_plan.json', encoding='utf-8')) plan_by_row = {g['row']: g for g in plan} wb = openpyxl.load_workbook(SRC) ws = wb.active NCOL = 27 DATA_LO, DATA_HI = 3, 276 # real data (rows 277+ empty/junk) # ---- merge key maps for D(4) and E(5) ---- def covering(col, r): for m in ws.merged_cells.ranges: if m.min_col <= col <= m.max_col and m.min_row <= r <= m.max_row: return str(m) return None def keymap(col): km = {} for r in range(DATA_LO, DATA_HI+1): c = covering(col, r) km[r] = c if c else f'{get_column_letter(col)}{r}' return km DKEY = keymap(4) EKEY = keymap(5) def kind_fields(kind, total=None): if kind == 'site': return dict(L='사이트', M=None, N=None, O=None) if kind == 'bbs': return dict(L='게시판', M=(total if total is not None else 0), N='어문', O='미부착') # page / filter return dict(L='페이지', M=1, N='어문', O='미부착') # ---- build out_rows ---- # each: {'tpl': src_row, 'over': {col_letter:value}, 'klink': href_or_None, # 'dkey':, 'ekey':, 'fgrp':, 'ggrp':} out = [] fgrp_id = 0 for r in range(DATA_LO, DATA_HI+1): base = {'tpl': r, 'over': {}, 'klink': None, 'dkey': DKEY[r], 'ekey': EKEY[r], 'fgrp': None, 'ggrp': None} out.append(base) if r not in plan_by_row: continue g = plan_by_row[r] fgrp_id += 1 if g.get('slave'): leaves = g['leaves'] l1 = leaves[0] f = kind_fields('filter') # boards -> 게시판 base['over'] = {'G': l1['btxt'], 'H': l1['ctxt'], 'K': l1['url'], 'L': '게시판', 'M': l1['total'], 'N': '어문', 'O': '미부착'} base['klink'] = l1['url'] base['fgrp'] = ('F', fgrp_id) base['ggrp'] = ('G', fgrp_id, l1['b']) for lf in leaves[1:]: nr = {'tpl': r, 'dkey': DKEY[r], 'ekey': EKEY[r], 'over': {'C': '서천군', 'F': None, 'G': lf['btxt'], 'H': lf['ctxt'], 'K': lf['url'], 'L': '게시판', 'M': lf['total'], 'N': '어문', 'O': '미부착'}, 'klink': lf['url'], 'fgrp': ('F', fgrp_id), 'ggrp': ('G', fgrp_id, lf['b'])} out.append(nr) else: tabs = g['tabs'] midx = next((i for i,t in enumerate(tabs) if t['norm']==g['norm']), None) if midx is not None: base['over']['G'] = tabs[midx]['label'] others = [t for i,t in enumerate(tabs) if i != midx] else: t0 = tabs[0] kf = kind_fields(t0['kind'], t0.get('total')) base['over'].update({'G': t0['label'], 'K': t0['href'], **kf}) base['klink'] = t0['href'] others = tabs[1:] ffull = g['F'] base['fgrp'] = ('F', fgrp_id) if ffull is not None else None for t in others: kf = kind_fields(t['kind'], t.get('total')) nr = {'tpl': r, 'dkey': DKEY[r], 'ekey': EKEY[r], 'over': {'C': '서천군', 'F': None, 'G': t['label'], 'K': t['href'], **kf}, 'klink': t['href'], 'fgrp': (('F', fgrp_id) if ffull is not None else None), 'ggrp': None} out.append(nr) print('total out data rows:', len(out)) # ---- write into a NEW worksheet ---- nwb = openpyxl.Workbook() nws = nwb.active nws.title = ws.title # column widths for col, dim in ws.column_dimensions.items(): nws.column_dimensions[col].width = dim.width nws.column_dimensions[col].hidden = dim.hidden nws.sheet_format = copy(ws.sheet_format) try: nws.freeze_panes = ws.freeze_panes except Exception: pass def copy_cell(srow, scol, drow, dcol, value='__keep__', link='__none__'): s = ws.cell(srow, scol); d = nws.cell(drow, dcol) if s.has_style: d.font = copy(s.font) d.fill = copy(s.fill) d.border = copy(s.border) d.alignment = copy(s.alignment) d.protection = copy(s.protection) d.number_format = s.number_format d.value = s.value if value == '__keep__' else value if link != '__none__': if link: d.hyperlink = link else: if s.hyperlink is not None: d.hyperlink = copy(s.hyperlink) return d # header rows 1-2 for r in (1,2): h = ws.row_dimensions[r].height if h: nws.row_dimensions[r].height = h for c in range(1, NCOL+1): copy_cell(r, c, r, c) # data rows LETTER2COL = {get_column_letter(c): c for c in range(1, NCOL+1)} for i, ro in enumerate(out): drow = 3 + i tpl = ro['tpl'] h = ws.row_dimensions[tpl].height if h: nws.row_dimensions[drow].height = h over = ro['over'] overcols = {LETTER2COL[k]: v for k, v in over.items()} for c in range(1, NCOL+1): if c == 11 and ro['klink'] is not None: copy_cell(tpl, c, drow, c, value=over.get('K', ws.cell(tpl,c).value), link=ro['klink']) elif c in overcols: copy_cell(tpl, c, drow, c, value=overcols[c], link='' if c==11 else '__none__') else: copy_cell(tpl, c, drow, c) # B renumber nws.cell(drow, 2).value = i + 1 ro['drow'] = drow # ---- header merges ---- for m in ws.merged_cells.ranges: if m.max_row <= 2: nws.merge_cells(str(m)) # ---- recompute D/E merges ---- def merge_runs(keyattr, col): i = 0; n = len(out) while i < n: j = i while j+1 < n and out[j+1][keyattr] == out[i][keyattr]: j += 1 if j > i: nws.merge_cells(start_row=out[i]['drow'], start_column=col, end_row=out[j]['drow'], end_column=col) i = j+1 merge_runs('dkey', 4) merge_runs('ekey', 5) # ---- F merges (per expanded group) ---- from collections import defaultdict fg = defaultdict(list); gg = defaultdict(list) for ro in out: if ro.get('fgrp'): fg[ro['fgrp']].append(ro['drow']) if ro.get('ggrp'): gg[ro['ggrp']].append(ro['drow']) for k, rows in fg.items(): if len(rows) > 1: nws.merge_cells(start_row=min(rows), start_column=6, end_row=max(rows), end_column=6) for k, rows in gg.items(): if len(rows) > 1: nws.merge_cells(start_row=min(rows), start_column=7, end_row=max(rows), end_column=7) nwb.save(OUT) print('SAVED', OUT, 'rows=', 2+len(out))