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